/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.*;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.cache.DictCache;
import org.springblade.common.config.ProjectApiConfiguration;
import org.springblade.common.constant.BaseRoleConstant;
import org.springblade.common.mq.MqMessageService;
import org.springblade.common.utils.CommonUtil;
import org.springblade.common.utils.delayed.DelayedSend;
import org.springblade.common.utils.message.MessageCenterUtils;
import org.springblade.common.utils.message.domain.MessageParam;
import org.springblade.common.utils.message.domain.SendRequest;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.modules.admin.dto.*;
import org.springblade.modules.admin.dto.feishu.ElevatorMaintainCostPlanListDTO;
import org.springblade.modules.admin.dto.feishu.FeiShuUserDeptElevatorWxPlanDTO;
import org.springblade.modules.admin.dto.feishu.UserPerformanceMonthPlanListDTO;
import org.springblade.modules.admin.dto.plan.*;
import org.springblade.modules.admin.dto.plan.score.PlanScoreSearchDTO;
import org.springblade.modules.admin.dto.rescue.RescueNoticeConfirmDTO;
import org.springblade.modules.admin.dto.rescue.TlwMalfunctionRescueDTO;
import org.springblade.modules.admin.dto.sugar.BiWxPlanListDTO;
import org.springblade.modules.admin.dto.sugar.ElevatorByCollectListDTO;
import org.springblade.modules.admin.dto.sugar.SugarSearchDTO;
import org.springblade.modules.admin.dto.tlw.TlwPlanQueryDTO;
import org.springblade.modules.admin.dto.tlw.TlwRescuePlanChangeStateDTO;
import org.springblade.modules.admin.dto.video.TicketFeedBackDTO;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.*;
import org.springblade.modules.admin.event.TlwRescueInfoEvent;
import org.springblade.modules.admin.mapper.ElevatorPlanStatusMapper;
import org.springblade.modules.admin.mapper.MaintainMalfunctionHistoryMapper;
import org.springblade.modules.admin.mapper.MaintainPlanContentMapper;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.service.approve.strategy.ApproveBusinessFlow;
import org.springblade.modules.admin.service.approve.strategy.ApproveBusinessStrategy;
import org.springblade.modules.admin.service.feishu.ILarkMaintenanceService;
import org.springblade.modules.admin.service.suger.IEstateTlwScreenService;
import org.springblade.modules.admin.utils.*;
import org.springblade.modules.admin.vo.*;
import org.springblade.modules.admin.vo.defert.PlanConfigVo;
import org.springblade.modules.admin.vo.feishu.LarkRepairVO;
import org.springblade.modules.admin.vo.image.by.ByPlanImageListVO;
import org.springblade.modules.admin.vo.image.by.ByPlanImageSimpleListVO;
import org.springblade.modules.admin.vo.openapi.SidaMaintainPlanVO;
import org.springblade.modules.admin.vo.plan.*;
import org.springblade.modules.admin.vo.plan.store.PlanScoreListVo;
import org.springblade.modules.admin.vo.plan.store.PlanScoreMonthVo;
import org.springblade.modules.admin.vo.plan.store.UserStorePlanInfoVo;
import org.springblade.modules.admin.vo.rescue.UserInfoVo;
import org.springblade.modules.admin.vo.sugar.ElevatorDetailScreenVO;
import org.springblade.modules.admin.vo.sugar.MaintainTypeDataVO;
import org.springblade.modules.admin.vo.sugar.MalfunctionScreenVO;
import org.springblade.modules.admin.vo.sugar.*;
import org.springblade.modules.admin.vo.sugar.bi.BILarkRescueCountSourceVO;
import org.springblade.modules.admin.vo.task.*;
import org.springblade.modules.admin.vo.tlw.TlwByPlanVO;
import org.springblade.modules.admin.vo.weixin.CommonVO;
import org.springblade.modules.admin.vo.weixin.MessageTemplateVO;
import org.springblade.modules.admin.vo.weixin.MessageVO;
import org.springblade.modules.admin.vo.weixin.MiniprogramVO;
import org.springblade.modules.admin.wrapper.MaintainMalfunctionWorkWrapper;
import org.springblade.modules.admin.wrapper.MaintainPlanContentWrapper;
import org.springblade.modules.notice.annotation.MsgNotice;
import org.springblade.modules.notice.core.event.NoticeEvent;
import org.springblade.modules.notice.dto.NoticeTaskParam;
import org.springblade.modules.notice.enums.BusinessType;
import org.springblade.modules.notice.enums.TriggerConditionEnum;
import org.springblade.modules.notice.enums.TriggerNodeEnum;
import org.springblade.modules.system.entity.*;
import org.springblade.modules.system.service.*;
import org.springblade.modules.system.vo.SimpleUserVO;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.springblade.modules.admin.service.BuildingService.PROJECT_TYPE_CODE;

/**
 * 维保任务表 服务实现类
 *
 * @author ysq
 * @since 2022-03-09
 */
@Slf4j
@Service
@AllArgsConstructor
public class MaintainPlanContentServiceImpl extends BaseServiceImpl<MaintainPlanContentMapper, MaintainPlanContent> implements IMaintainPlanContentService {

	/**
	 * 短信 code
	 */
	private static final String APP_ID = "wxb8dc0f5451620604";
	private static final String PAGE_PATH = "pages/index/index";
	/**
	 * 批量删除导出出错数据key
	 */
	private static final String PLAN_BATCH_EXCEL_ERR_CACHE = "plan:batch:excel:err:";
	private static final String PLAN_TEMPLATE_BATCH_EXCEL_ERR_CACHE = "plan:template:batch:excel:err:";
	private final IMaintainPlanContentUserService planContentUserService;
	private final IMaintainPlanTemplateService planTemplateService;
	private final IMaintainPlanRelationService planRelationService;
	private final IMaintainSignService maintainSignService;
	private final MaintainMalfunctionHistoryMapper maintainMalfunctionHistoryMapper;
	private final IMaintainMalfunctionWorkService malfunctionWorkService;
	private final MaintainMalfunctionLogService malfunctionLogService;
	private final IMaintainPlanContentExtService maintainPlanContentExtService;
	private final UserWeixinService userWeixinService;
	private final WeiXinUtil weiXinUtil;
	private final ElevatorService elevatorService;
	private final IElevatorDetailService elevatorDetailService;
	private final ElevatorPlanStatusMapper elevatorPlanStatusMapper;
	private final ElevatorMalfunctionDayService elevatorMalfunctionDayService;
	private final IMaintainSignService signService;
	private final IDictService dictService;
	private final IMaintainPlanScoreRecordService maintainPlanScoreRecordService;
	private final IDeptService deptService;
	private final IMaintainPlanConfigService maintainPlanConfigService;
	private final IMaintainPlanDeferHistoryService maintainPlanDeferHistoryService;
	private final MaintainTaskPlanService maintainTaskPlanService;
	private final IMaintainMalfunctionRescueService maintainMalfunctionRescueService;
	private final ElevatorPrincipalUserService elevatorPrincipalUserService;
	private final IBuildingContactsService buildingContactsService;
	private final BuildingService buildingService;
	private final AsyncUtils asyncUtils;
	private final ProjectApiConfiguration projectApiConfiguration;
	private final Environment environment;
	private final IEstateElevatorUserService estateElevatorUserService;
	private final ITenantService tenantService;
	private final IElevatorPlanStrategyService elevatorPlanStrategyService;
	private final MqMessageService mqMessageService;
	private final ElevatorDeviceService elevatorDeviceService;
	private final IMaintainPlanRelationService maintainPlanRelationService;
	private final IMaintainPlanCheckService maintainPlanCheckService;
	private final BladeRedis bladeRedis;
	private final IEstateTlwScreenService estateTlwScreenService;
	private final IBuildingExtService buildingExtService;
	private final DelayedSend delayedSend;
	private final TenantConfigService tenantConfigService;
	private final IElevatorStopLogService elevatorStopLogService;
	private final IElevatorWxNumberService elevatorWxNumberService;
	private final IElevatorWxNumberStatisticService elevatorWxNumberStatisticService;
	private final ApplicationEventPublisher eventPublisher;
	private final IElevatorRepeatFaultConfigService elevatorRepeatFaultConfigService;
	private final IRoleService roleService;
	private MessageCenterUtils messageCenterUtils;
	private final ElevatorPrincipalDeptService elevatorPrincipalDeptService;
	private final IWxFaultCauseService wxFaultCauseService;
	@Lazy
	private final IRescueNoticeService rescueNoticeService;
	private final IEstateTlwScreenService tlwScreenService;

	/**
	 * 物业发送模板消息路径
	 */
	private final String official_accounts_push_url = "/tlw/alarm/official/accounts/push";
	private IUserService userService;

	private final ILarkMaintenanceService larkMaintenanceService;

	private static BigDecimal divide(Object obj1, Object obj2, int i) {

		BigDecimal bigDecimal = Convert.toBigDecimal(obj2);
		BigDecimal bigDecimal2 = Convert.toBigDecimal(obj1);
		if (bigDecimal.equals(Convert.toBigDecimal(0)) || bigDecimal2.equals(Convert.toBigDecimal(0))) {
			return Convert.toBigDecimal(0);
		}
		BigDecimal divideBigDecimal = bigDecimal2.divide(bigDecimal, 2, BigDecimal.ROUND_HALF_UP);
		return divideBigDecimal;
	}


	/**
	 * String 转 LocalDate
	 *
	 * @param date yyyy-MM-dd
	 * @return
	 */
	public static LocalDate localDate(String date) {
		return LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
	}

	/**
	 * Date 转 LocalDate
	 *
	 * @param date yyyy-MM-dd
	 * @return
	 */
	public static LocalDate localDate(Date date) {
		return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
	}

	/**
	 * 获取指定时间段所有月份
	 *
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static List<Date> getDatesBetweenByMonth(LocalDate startDate, LocalDate endDate) {
		LocalDate finalStartDate = startDate.with(TemporalAdjusters.firstDayOfMonth());
		LocalDate finalEndDate = endDate.with(TemporalAdjusters.firstDayOfMonth());
		long numOfDaysBetween = ChronoUnit.MONTHS.between(finalStartDate, finalEndDate) + 1;
		return IntStream.iterate(0, i -> i + 1)
			.limit(numOfDaysBetween)
			.mapToObj(i -> Date.from(finalStartDate.plusMonths(i).atStartOfDay(ZoneId.systemDefault()).toInstant()))
			.collect(Collectors.toList());
	}

	public static void main(String[] args) {
		System.err.println(DateUtil.betweenDay(DateUtil.parse("2025-07-24 00:00:00","yyyy-MM-dd HH:mm:ss"),DateUtil.offsetSecond(DateUtil.parse("2025-07-25 23:59:59","yyyy-MM-dd HH:mm:ss"),1),Boolean.FALSE));
	}

	@Override
	public IPage<MaintainPlanContentVO> selectMaintainPlanContentPage(IPage<MaintainPlanContentVO> page, MaintainPlanContentVO maintainPlanContent) {
		return page.setRecords(baseMapper.selectMaintainPlanContentPage(page, maintainPlanContent));
	}

	@Override
	public void deleteDateIntervalTask(List<Long> elevatorIds, Date startTime, Date endTime) {
		LambdaQueryWrapper<MaintainPlanContent> wrapper = Wrappers.lambdaQuery();
		wrapper.in(MaintainPlanContent::getElevatorId, elevatorIds)
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
			.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
			.eq(MaintainPlanContent::getIsDeleted, BladeConstant.DB_NOT_DELETED);
		if (startTime != null) {
			wrapper.ge(MaintainPlanContent::getPlanDate, startTime);
		}
		if (endTime != null) {
			wrapper.le(MaintainPlanContent::getPlanDate, endTime);
		}

		List<MaintainPlanContent> planContentList = baseMapper.selectList(wrapper);
		if (Func.isEmpty(planContentList)) {
			return;
		}
		List<Long> planIds = planContentList.stream().map(t -> t.getId()).collect(Collectors.toList());
		deleteTaskByPlanId(planIds, elevatorIds);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteTaskByPlanId(List<Long> planIds, List<Long> elevatorIds) {
		if (Func.isEmpty(planIds)) {
			return;
		}
		if (Func.isEmpty(elevatorIds)) {
			elevatorIds = baseMapper.selectBatchIds(planIds).stream().map(t -> t.getElevatorId()).collect(Collectors.toList());
		}
		// 删除任务计划
		this.deleteLogic(planIds);
		// 删除任务关联人员关系
		planContentUserService.remove(Wrappers.<MaintainPlanContentUser>lambdaQuery().in(MaintainPlanContentUser::getPlanId, planIds));
		// 删除任务关联模板关系
		planTemplateService.remove(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, planIds));
		// 删除任务关联任务关系
		planRelationService.remove(Wrappers.<MaintainPlanRelation>lambdaQuery().in(MaintainPlanRelation::getOwnPlanId, planIds));
		// 删除任务扩展表
		maintainPlanContentExtService.remove(Wrappers.<MaintainPlanContentExt>lambdaQuery().in(MaintainPlanContentExt::getPlanId, planIds));
		// 删除维保任务延期历史日志
		maintainPlanDeferHistoryService.remove(Wrappers.<MaintainPlanDeferHistory>lambdaQuery().in(MaintainPlanDeferHistory::getPlanId, planIds));
		// 更新电梯最新排期策略
		elevatorPlanStrategyService.planStrategyUpdate(elevatorIds);
	}

	@Override
	public void speedCreatePlanBatch(SpeedPlanParam speedPlanParam) {
		MaintainTaskPlan taskPlan = baseMapper.getTaskPlanById(speedPlanParam.getTaskPlanId(), AuthUtil.getTenantId());
		if (taskPlan == null) {
			throw new ServiceException("未找到对应任务计划！");
		}

		// 1、查询占坑周期规则模板
		MaintainTaskPlanTemplate occupyTaskPlanTemplate = baseMapper.findOccupyRule(speedPlanParam.getTaskPlanId(), AuthUtil.getTenantId());
		if (occupyTaskPlanTemplate == null) {
			throw new ServiceException("未找到保养周期模板！");
		}

		// 2、生成占坑任务计划
		TreeMap<Date, LinkedList<OccupyPlanVO>> occupyPlanTreeMap = generateOccupyPlan(occupyTaskPlanTemplate, speedPlanParam);

		// 3、生成最终任务计划
		generateTaskPlan(speedPlanParam, occupyPlanTreeMap, occupyTaskPlanTemplate);

		// 4、更新电梯保养计划策略
		elevatorPlanStrategyService.planStrategyUpdate(Func.toLongList(speedPlanParam.getElevatorIds()));
	}

	/**
	 * 生成占坑任务计划
	 *
	 * @param template       占坑周期模板
	 * @param speedPlanParam
	 * @return
	 */
	private TreeMap<Date, LinkedList<OccupyPlanVO>> generateOccupyPlan(MaintainTaskPlanTemplate template, SpeedPlanParam speedPlanParam) {
		TreeMap<Date, LinkedList<OccupyPlanVO>> planTreeMap = new TreeMap<>(new PlanDateComparator());
		Date tempDate = speedPlanParam.getFirstTime();
		do {
			LinkedList<OccupyPlanVO> plans = new LinkedList<>();
			OccupyPlanVO plan = new OccupyPlanVO();
			plan.setUserIds(speedPlanParam.getUserIds());
			plan.setTypeStatus(TaskStatusEnum.BY_DISABLE.getTypeStatus());
			plan.setPlanDate(tempDate);
			plan.setTemplateIds(template.getTemplateId());
			plan.setPriority(template.getPriority());
			plans.add(plan);
			planTreeMap.put(tempDate, plans);
			tempDate = DateUtil.offsetDay(tempDate, Integer.valueOf(template.getPeriod()));
		} while (DateUtil.between(tempDate, speedPlanParam.getEndTime(), DateUnit.DAY, false) >= 0);
		return planTreeMap;
	}

	/**
	 * 根据排期规则生成计划，规则如下：
	 * 一、优先级相同（坑位追加排期）：
	 * 1、周期：根据周期往前找到最近的坑位
	 * 2、固定月份：从坑位找到对应月份第一个坑位（未找到不填）
	 * 二、优先级不同（坑位排期全部覆盖）：
	 * 1、周期：根据周期往前找到最近的坑位
	 * 2、固定月份：从坑位找到对应月份第一个坑位（未找到不填）
	 *
	 * @param speedPlanParam
	 * @param occupyPlanTreeMap      占坑任务表
	 * @param occupyTaskPlanTemplate 占坑模板
	 */
	private void generateTaskPlan(SpeedPlanParam speedPlanParam, TreeMap<Date, LinkedList<OccupyPlanVO>> occupyPlanTreeMap, MaintainTaskPlanTemplate occupyTaskPlanTemplate) {
		// 查询任务计划模板（排除占坑模板）
		LinkedList<MaintainTaskPlanTemplate> taskPlanTemplateList = baseMapper.findTaskPlanIgnoreOccupyPlan(speedPlanParam.getTaskPlanId(), occupyTaskPlanTemplate.getId(), AuthUtil.getTenantId());
		for (MaintainTaskPlanTemplate template : taskPlanTemplateList) {
			if (template.getPeriodType() == 1) {
				// 周期
				replaceOrAddPeriodPlan(speedPlanParam, occupyPlanTreeMap, template.getPriority(), Integer.valueOf(template.getPeriod()), template.getTemplateId());
			} else {
				// 固定月份
				replaceOrAddMonthPlan(occupyPlanTreeMap, template.getPriority(), template.getPeriod(), template.getTemplateId());
			}
		}

		// 所有任务集合
		LinkedList<OccupyPlanVO> allPlanList = new LinkedList<>();
		for (LinkedList<OccupyPlanVO> plans : occupyPlanTreeMap.values()) {
			allPlanList.addAll(plans);
		}

		List<MaintainPlanTemplate> batchPlanTemplateList = new ArrayList<>();
		List<MaintainPlanContentUser> batchPlanContentUserList = new ArrayList<>();
		List<MaintainPlanContentExt> batchPlanContentExtList = new ArrayList<>();
		List<MaintainPlanDeferHistory> batchMaintainPlanDeferHistoryList = new ArrayList<>();
		List<Long> elevatorIds = Func.toLongList(speedPlanParam.getElevatorIds());

		//批量获取电梯的年审非年审状态
		final List<PlanConfigVo> planConfigVos = maintainPlanConfigService.getPlanConfigVo(elevatorIds);
		final Map<Long, PlanConfigVo> configMap = planConfigVos.stream().collect(Collectors.toMap(t -> t.getElevatorId(), t -> t, (t1, t2) -> t1));


		for (Long elevatorId : elevatorIds) {
			for (OccupyPlanVO occupyPlan : allPlanList) {
				MaintainPlanContent addPlan = new MaintainPlanContent();
				BeanUtils.copyProperties(occupyPlan, addPlan);
				addPlan.setElevatorId(elevatorId);
				if (this.save(addPlan)) {
					// 任务关联模板
					List<Long> templateIds = Func.toLongList(occupyPlan.getTemplateIds());
					for (int i = 0; i < templateIds.size(); i++) {
						MaintainPlanTemplate maintainPlanTemplate = new MaintainPlanTemplate();
						maintainPlanTemplate.setPlanId(addPlan.getId());
						maintainPlanTemplate.setTemplateId(templateIds.get(i));
						maintainPlanTemplate.setSort(i);
						batchPlanTemplateList.add(maintainPlanTemplate);

					}
					// 任务关联人员
					List<Long> userIds = Func.toLongList(occupyPlan.getUserIds());
					for (Long userId : userIds) {
						MaintainPlanContentUser planContentUser = new MaintainPlanContentUser();
						planContentUser.setPlanId(addPlan.getId());
						planContentUser.setUserId(userId);
						batchPlanContentUserList.add(planContentUser);
					}

					// 任务扩展表
					MaintainPlanContentExt contentExt = new MaintainPlanContentExt();
					contentExt.setPlanId(addPlan.getId());
					contentExt.setAuditType(configMap.get(elevatorId).getAuditType());
					batchPlanContentExtList.add(contentExt);

					// 维保任务延期历史日志表
					MaintainPlanDeferHistory maintainPlanDeferHistory = this.handlePlanDeferHistory(addPlan.getId(), addPlan.getPlanDate(), elevatorId, configMap.get(elevatorId));
					batchMaintainPlanDeferHistoryList.add(maintainPlanDeferHistory);

					// 插入任务跟踪信息
					malfunctionLogService.insertWorkLog(addPlan.getId(), WorkTrackStatusEnum.DISTRIBUTION_WORK.getTypeName(), addPlan.getStatus());
				}
			}
		}

		planTemplateService.saveBatch(batchPlanTemplateList);
		planContentUserService.saveBatch(batchPlanContentUserList);
		maintainPlanContentExtService.saveBatch(batchPlanContentExtList);
		maintainPlanDeferHistoryService.saveBatch(batchMaintainPlanDeferHistoryList);
	}

	/**
	 * 根据周期规则，在坑位表往前找到最近的坑位
	 *
	 * @param speedPlanParam
	 * @param occupyPlanTreeMap
	 * @param priority          优先级
	 * @param period            周期值
	 * @param templateIds       替换的模板id
	 * @return
	 */
	private void replaceOrAddPeriodPlan(SpeedPlanParam speedPlanParam, TreeMap<Date, LinkedList<OccupyPlanVO>> occupyPlanTreeMap,
										int priority, int period, String templateIds) {
		// 根据period生成对应模板周期日期计划
		LinkedList<Date> planDates = new LinkedList<>();
		Date tempDate = speedPlanParam.getFirstTime();
		do {
			planDates.add(tempDate);
			tempDate = DateUtil.offsetDay(tempDate, period);
		} while (DateUtil.between(tempDate, speedPlanParam.getEndTime(), DateUnit.DAY, false) >= 0);

		for (Date planDate : planDates) {
			// 往前找到最近的坑位（获取集合内的key小于"planDate"的key）
			Date lowerKey = occupyPlanTreeMap.lowerKey(planDate);
			if (lowerKey != null) {
				handleOccupyPlan(occupyPlanTreeMap, lowerKey, priority, templateIds);
			}
		}
	}

	/**
	 * 根据给定月份，从坑位表找到第一个坑位(可能有多个，存在多年相同月份情况)
	 *
	 * @param occupyPlanTreeMap
	 * @param priority          优先级
	 * @param months            月份，多个逗号隔开
	 * @param templateIds       替换的模板id
	 * @return
	 */
	private void replaceOrAddMonthPlan(TreeMap<Date, LinkedList<OccupyPlanVO>> occupyPlanTreeMap, int priority, String months, String templateIds) {
		List<Integer> monthList = Func.toIntList(months);
		for (Integer month : monthList) {
			// 年份标记
			int yearFlag = 0;
			for (Date key : occupyPlanTreeMap.keySet()) {
				if ((DateUtil.month(key) + 1) == month && yearFlag != DateUtil.year(key)) {
					yearFlag = DateUtil.year(key);
					handleOccupyPlan(occupyPlanTreeMap, key, priority, templateIds);
				}
			}
		}
	}

	/**
	 * 根据优先级处理坑位数据
	 * 1、优先级相同追加
	 * 2、优先级不同覆盖（高覆盖低）
	 *
	 * @param occupyPlanTreeMap
	 * @param priority
	 * @param templateIds
	 */
	private void handleOccupyPlan(TreeMap<Date, LinkedList<OccupyPlanVO>> occupyPlanTreeMap, Date keyDate, int priority, String templateIds) {
		LinkedList<OccupyPlanVO> occupyPlanList = occupyPlanTreeMap.get(keyDate);
		OccupyPlanVO occupyPlan = occupyPlanList.getLast();
		if (priority == occupyPlan.getPriority()) {
			// 1、优先级相同: 坑位追加排期
			OccupyPlanVO copyPlan = new OccupyPlanVO();
			BeanUtils.copyProperties(occupyPlan, copyPlan);
			copyPlan.setTemplateIds(templateIds);
			occupyPlanList.add(copyPlan);
		} else {
			// 2、优先级不同: 坑位排期全部覆盖
			occupyPlanList.clear();
			occupyPlan.setTemplateIds(templateIds);
			occupyPlan.setPriority(priority);
			occupyPlanList.add(occupyPlan);
		}
	}

	/**
	 * 封装数据：维保任务延期历史日志
	 *
	 * @param planId
	 * @param planDate
	 * @param elevatorId
	 * @param planConfigVo
	 * @return
	 */
	private MaintainPlanDeferHistory handlePlanDeferHistory(Long planId, Date planDate, Long elevatorId, PlanConfigVo planConfigVo) {
		if (Func.isNull(planConfigVo)) {
			List<PlanConfigVo> planConfigList = maintainPlanConfigService.getPlanConfigVo(Arrays.asList(elevatorId));
			if (Func.isNotEmpty(planConfigList)) {
				planConfigVo = planConfigList.get(0);
			}
		}

		MaintainPlanDeferHistory maintainPlanDeferHistory = new MaintainPlanDeferHistory();
		maintainPlanDeferHistory.setPlanId(planId);
		maintainPlanDeferHistory.setPlanDate(planDate);
		BeanUtils.copyProperties(planConfigVo, maintainPlanDeferHistory);
		maintainPlanDeferHistory.setPlanType(planConfigVo.getAuditType());
		maintainPlanDeferHistory.setCreateUserName(AuthUtil.getUserName());
		return maintainPlanDeferHistory;
	}

	@Override
	public TreeMap<String, List<MaintainPlanContentVO>> getMaintainPlans(List<Long> elevatorIds, Integer year, List<Long> templateIds) {
		String tenantId = BladeConstant.ADMIN_TENANT_ID.equals(AuthUtil.getTenantId()) ? null : AuthUtil.getTenantId();
		List<MaintainPlanContentVO> list = baseMapper.getMaintainPlans(elevatorIds, year, MaintainPlanContentEnum.MAINTAIN_BY.getId(), tenantId);
		if (Func.isEmpty(list)) {
			return null;
		}
		// 高亮筛选
		if (Func.isNotEmpty(templateIds)) {
			list.forEach(plan -> {
				List<MaintainTemplateInfoVO> templates = plan.getTemplates().stream().filter(t -> templateIds.contains(t.getTemplateId())).collect(Collectors.toList());
				templates.forEach(t -> t.setHighlight(true));
			});
		}
		return list.stream().collect(Collectors.groupingBy((t -> DateUtil.format(t.getPlanDate(), "yyyy-MM-dd")), TreeMap::new, Collectors.toList()));
	}

	private boolean modifyPlanCheck(ModifyPlanVO modifyPlanVO) {
		if (Func.isEmpty(modifyPlanVO.getPlanIds())) {
			throw new ServiceException("参数：planIds不能为空！");
		}
		Long planId = modifyPlanVO.getPlanIds().get(0);
		MaintainPlanContent maintainPlanContent = baseMapper.selectById(planId);
		if (Func.isEmpty(maintainPlanContent)) {
			throw new ServiceException("更改的计划不存在！");
		}
		if (DateUtil.betweenDay(maintainPlanContent.getPlanDate(), DateUtil.date(), true) < 0) {
			throw new ServiceException("不能更改当天以前的排期计划！");
		}
		return true;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean modifyPlan(ModifyPlanVO modifyPlanVO) {
		MaintainPlanContent maintainPlanContent = baseMapper.selectById(modifyPlanVO.getPlanIds().get(0));
		if (Func.isEmpty(maintainPlanContent)) {
			throw new ServiceException("更改的计划不存在！");
		}
		if (!maintainPlanContent.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus())) {
			throw new ServiceException("工单状态已变更，请刷新页面！");
		}
		if (DateUtil.betweenDay(maintainPlanContent.getPlanDate(), DateUtil.date(), true) < 0) {
			throw new ServiceException("不能更改当天以前的排期计划！");
		} else {
			modifyPlanVO.setOldDate(maintainPlanContent.getPlanDate());
		}

		// 批量获取电梯的年审非年审状态
		final PlanConfigVo planConfig = maintainPlanConfigService.getPlanConfigVo(modifyPlanVO.getElevatorIds()).get(0);

		// 更新任务扩展表
		MaintainPlanContentExt contentExt = new MaintainPlanContentExt();
		contentExt.setAuditType(planConfig.getAuditType());
		contentExt.setDeferType(4);
		contentExt.setDeferCounts(0);
		contentExt.setDeferWarrant(4);
		contentExt.setDeferDescCounts(0);
		if (modifyPlanVO.getOldDate() != null && modifyPlanVO.getDate() != null) {
			String yyyyMM1 = DateUtil.format(modifyPlanVO.getOldDate(), "yyyyMM");
			String yyyyMM2 = DateUtil.format(modifyPlanVO.getDate(), "yyyyMM");
			if (!yyyyMM1.equals(yyyyMM2)) {
				contentExt.setScoreSpotCheckType(0);
			}
		}
		maintainPlanContentExtService.update(contentExt, Wrappers.<MaintainPlanContentExt>lambdaUpdate().in(MaintainPlanContentExt::getPlanId, modifyPlanVO.getPlanIds()));

		// 对比更改日期差值
		long diffDate = DateUtil.between(modifyPlanVO.getOldDate(), modifyPlanVO.getDate(), DateUnit.DAY, false);
		if (diffDate != 0) {
			// 更新任务保养日期
			MaintainPlanContent modifyPlan = new MaintainPlanContent();
			modifyPlan.setPlanDate(modifyPlanVO.getDate());
			this.update(modifyPlan, Wrappers.<MaintainPlanContent>lambdaUpdate().in(MaintainPlanContent::getId, modifyPlanVO.getPlanIds()));

			// 删除延期历史日志集合
			List<Long> removeDeferPlanIds = new ArrayList<>();
			removeDeferPlanIds.addAll(modifyPlanVO.getPlanIds());

			// 插入初始延期历史日志集合
			List<MaintainPlanDeferHistory> addPlanDeferHistoryList = new ArrayList<>();
			addPlanDeferHistoryList.add(this.handlePlanDeferHistory(modifyPlanVO.getPlanIds().get(0), modifyPlan.getPlanDate(), modifyPlanVO.getElevatorIds().get(0), planConfig));

			List<MaintainMalfunctionLog> logList = new ArrayList<>(100);
			// 改期后的所有计划滚动调整
			if (modifyPlanVO.getType() == 0) {
				List<MaintainPlanContent> modifyPlanList = this.list(Wrappers.<MaintainPlanContent>lambdaQuery()
					.in(MaintainPlanContent::getElevatorId, modifyPlanVO.getElevatorIds())
					.gt(MaintainPlanContent::getPlanDate, maintainPlanContent.getPlanDate())
					.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
					.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
					.eq(MaintainPlanContent::getIsDeleted, BladeConstant.DB_NOT_DELETED)
					.notIn(MaintainPlanContent::getId, modifyPlanVO.getPlanIds())
				);
				if (Func.isNotEmpty(modifyPlanList)) {
					//清除日志
					final List<Long> ids = modifyPlanList.stream().map(MaintainPlanContent::getId).collect(Collectors.toList());

					malfunctionLogService.remove(Wrappers.<MaintainMalfunctionLog>lambdaQuery().in(MaintainMalfunctionLog::getPlanId, ids));
					for (MaintainPlanContent plan : modifyPlanList) {
						// 变更改期后的日期
						plan.setPlanDate(DateUtil.offsetDay(plan.getPlanDate(), Integer.valueOf(String.valueOf(diffDate))));
						// 添加新的延期历史日志
						addPlanDeferHistoryList.add(this.handlePlanDeferHistory(plan.getId(), plan.getPlanDate(), plan.getElevatorId(), planConfig));

						MaintainMalfunctionLog maintainMalfunctionLog = new MaintainMalfunctionLog();
						maintainMalfunctionLog.setPlanId(plan.getId());
						maintainMalfunctionLog.setTypeName("分配计划");
						maintainMalfunctionLog.setPlanStatus(1);
						maintainMalfunctionLog.setExecutor(AuthUtil.getNickName());
						maintainMalfunctionLog.setExecutorId(AuthUtil.getUserId());
						logList.add(maintainMalfunctionLog);
					}
					this.updateBatchById(modifyPlanList);
					List<Long> modifyPlanIds = modifyPlanList.stream().map(t -> t.getId()).collect(Collectors.toList());
					// 删除旧的延期历史日志
					removeDeferPlanIds.addAll(modifyPlanIds);
					// 更新扩展表
					maintainPlanContentExtService.update(contentExt, Wrappers.<MaintainPlanContentExt>lambdaUpdate().in(MaintainPlanContentExt::getPlanId, modifyPlanIds));


				}
			} else if (modifyPlanVO.getType() == 1) {
				for (Long planId : modifyPlanVO.getPlanIds()) {
					malfunctionLogService.remove(Wrappers.<MaintainMalfunctionLog>lambdaQuery().in(MaintainMalfunctionLog::getPlanId, modifyPlanVO.getPlanIds()));
					MaintainMalfunctionLog maintainMalfunctionLog = new MaintainMalfunctionLog();
					maintainMalfunctionLog.setPlanId(planId);
					maintainMalfunctionLog.setTypeName("分配计划");
					maintainMalfunctionLog.setPlanStatus(1);
					maintainMalfunctionLog.setExecutor(AuthUtil.getNickName());
					maintainMalfunctionLog.setExecutorId(AuthUtil.getUserId());
					logList.add(maintainMalfunctionLog);
				}
			}

			//重新保存日志
			if (logList.size() > 0) {
				final List<List<MaintainMalfunctionLog>> partition = Lists.partition(logList, 30);
				for (List<MaintainMalfunctionLog> list : partition) {
					malfunctionLogService.saveBatch(list);
				}
			}
			maintainPlanDeferHistoryService.remove(Wrappers.<MaintainPlanDeferHistory>lambdaQuery().in(MaintainPlanDeferHistory::getPlanId, removeDeferPlanIds));
			maintainPlanDeferHistoryService.saveBatch(addPlanDeferHistoryList);
		}

		// 更新电梯保养计划策略
		elevatorPlanStrategyService.planStrategyUpdate(modifyPlanVO.getElevatorIds());

		return true;
	}

	@Override
	public boolean modifyTemplate(ModifyPlanVO modifyPlanVO) {
		Long planId = modifyPlanVO.getPlanIds().get(0);
		MaintainPlanContent maintainPlanContent = baseMapper.selectById(planId);
		if (Func.isEmpty(maintainPlanContent)) {
			throw new ServiceException("更改的计划不存在！");
		}
		if (!maintainPlanContent.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus())) {
			throw new ServiceException("工单状态已变更，请刷新页面！");
		}
		if (DateUtil.betweenDay(maintainPlanContent.getPlanDate(), DateUtil.date(), true) < 0) {
			throw new ServiceException("不能更改当天以前的排期计划！");
		}

		List<Long> removePlanTemplateIds = new ArrayList<>(16);
		List<MaintainPlanTemplate> batchPlanTemplateList = new ArrayList<>(16);
		// 查询当前关联模板数据
		List<MaintainPlanTemplate> planTemplateList = planTemplateService.list(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, modifyPlanVO.getPlanIds()));
		if (Func.isEmpty(planTemplateList)) {
			throw new ServiceException("数据非法!");
		}
		removePlanTemplateIds.addAll(planTemplateList.stream().map(t -> t.getId()).collect(Collectors.toList()));
		// 插入编辑的任务关联模板
		for (int i = 0; i < modifyPlanVO.getTemplateIds().size(); i++) {
			MaintainPlanTemplate maintainPlanTemplate = new MaintainPlanTemplate();
			maintainPlanTemplate.setPlanId(modifyPlanVO.getPlanIds().get(0));
			maintainPlanTemplate.setTemplateId(modifyPlanVO.getTemplateIds().get(i));
			maintainPlanTemplate.setSort(i);
			batchPlanTemplateList.add(maintainPlanTemplate);
		}

		// 改期后的所有计划滚动调整
		if (modifyPlanVO.getType() == 0) {
			List<MaintainPlanContent> modifyPlanList = this.list(Wrappers.<MaintainPlanContent>lambdaQuery()
				.in(MaintainPlanContent::getElevatorId, modifyPlanVO.getElevatorIds())
				.gt(MaintainPlanContent::getPlanDate, maintainPlanContent.getPlanDate())
				.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
				.eq(MaintainPlanContent::getIsDeleted, BladeConstant.DB_NOT_DELETED)
				.notIn(MaintainPlanContent::getId, planId)
			);
			if (Func.isNotEmpty(modifyPlanList)) {
				List<Long> modifyPlanIds = modifyPlanList.stream().map(t -> t.getId()).collect(Collectors.toList());
				List<MaintainPlanTemplate> modifyPlanTemplateList = planTemplateService.list(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, modifyPlanIds));
				if (Func.isNotEmpty(modifyPlanTemplateList)) {
					removePlanTemplateIds.addAll(modifyPlanTemplateList.stream().map(t -> t.getId()).collect(Collectors.toList()));
				}
				// 插入编辑的任务关联模板
				modifyPlanIds.forEach(modifyPlanId -> {
					for (int i = 0; i < modifyPlanVO.getTemplateIds().size(); i++) {
						MaintainPlanTemplate maintainPlanTemplate = new MaintainPlanTemplate();
						maintainPlanTemplate.setPlanId(modifyPlanId);
						maintainPlanTemplate.setTemplateId(modifyPlanVO.getTemplateIds().get(i));
						maintainPlanTemplate.setSort(i);
						batchPlanTemplateList.add(maintainPlanTemplate);
					}
				});
			}
		}

		return planTemplateService.deleteLogic(removePlanTemplateIds)
			&& planTemplateService.saveBatch(batchPlanTemplateList);
	}

	@Override
	public boolean modifyPlanUser(ModifyPlanUserDTO modifyPlanUserDTO) {
		Long planId = modifyPlanUserDTO.getPlanIds().get(0);
		MaintainPlanContent maintainPlanContent = baseMapper.selectById(planId);
		if (Func.isEmpty(maintainPlanContent)) {
			throw new ServiceException("更改的计划不存在！");
		}
		if (!maintainPlanContent.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus())) {
			throw new ServiceException("工单状态已变更，请刷新页面！");
		}
		if (DateUtil.betweenDay(maintainPlanContent.getPlanDate(), DateUtil.date(), true) < 0) {
			throw new ServiceException("不能更改当天以前的排期计划！");
		}


		List<Long> delPlanUserIds = new ArrayList<>();
		List<MaintainPlanContentUser> addPlanUserList = new ArrayList<>();
		List<MaintainPlanContentUser> planContentUserList = planContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().eq(MaintainPlanContentUser::getPlanId, planId));
		if (Func.isNotEmpty(planContentUserList)) {
			List<Long> planUserIds = planContentUserList.stream().map(t -> t.getId()).collect(Collectors.toList());
			delPlanUserIds.addAll(planUserIds);
			for (Long userId : modifyPlanUserDTO.getUserIds()) {
				MaintainPlanContentUser addPlanUser = new MaintainPlanContentUser();
				addPlanUser.setPlanId(planId);
				addPlanUser.setUserId(userId);
				addPlanUserList.add(addPlanUser);
			}
		}

		// 改期后的所有计划滚动调整
		if (modifyPlanUserDTO.getType() == 0) {
			List<MaintainPlanContent> modifyPlanList = this.list(Wrappers.<MaintainPlanContent>lambdaQuery()
				.in(MaintainPlanContent::getElevatorId, maintainPlanContent.getElevatorId())
				.ge(MaintainPlanContent::getPlanDate, maintainPlanContent.getPlanDate())
				.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
				.eq(MaintainPlanContent::getIsDeleted, BladeConstant.DB_NOT_DELETED)
				.notIn(MaintainPlanContent::getId, planId)
			);

			if (Func.isNotEmpty(modifyPlanList)) {
				// 查出之前的排期执行人，并删除
				List<Long> modifyPlanIds = modifyPlanList.stream().map(t -> t.getId()).collect(Collectors.toList());
				List<MaintainPlanContentUser> modifyPlanUserList = planContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().in(MaintainPlanContentUser::getPlanId, modifyPlanIds));
				if (Func.isNotEmpty(modifyPlanIds)) {
					delPlanUserIds.addAll(modifyPlanUserList.stream().map(t -> t.getId()).collect(Collectors.toList()));
				}

				// 插入新的排期执行人
				for (Long modifyPlanId : modifyPlanIds) {
					for (Long userId : modifyPlanUserDTO.getUserIds()) {
						MaintainPlanContentUser addPlanUser = new MaintainPlanContentUser();
						addPlanUser.setPlanId(modifyPlanId);
						addPlanUser.setUserId(userId);
						addPlanUserList.add(addPlanUser);
					}
				}
			}
		}

		return planContentUserService.deleteLogic(delPlanUserIds) && planContentUserService.saveBatch(addPlanUserList);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Integer batchModifyPlanUser(BatchModifyPlanUserDTO planUserDTO) {
		// 查询电梯今日或今日以后「待开始」状态的保养工单
		List<MaintainPlanContent> planContentList = list(Wrappers.<MaintainPlanContent>lambdaQuery()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
			.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
			.in(MaintainPlanContent::getElevatorId, planUserDTO.getElevatorIds())
			.ge(MaintainPlanContent::getPlanDate, DateUtil.today()));
		if (Func.isEmpty(planContentList)) {
			return 0;
		}
		// 查询工单对应执行人
		List<Long> planIds = planContentList.stream().map(t -> t.getId()).collect(Collectors.toList());
		BatchModifyPlanUserByPlanIdsDTO planUserByPlanIdsDTO = new BatchModifyPlanUserByPlanIdsDTO();
		BeanUtils.copyProperties(planUserDTO, planUserByPlanIdsDTO);
		planUserByPlanIdsDTO.setPlanIds(planIds);
		return this.batchModifyPlanUserByPlanIds(planUserByPlanIdsDTO);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Integer batchModifyPlanUserByPlanIds(BatchModifyPlanUserByPlanIdsDTO planUserDTO) {
		List<MaintainPlanContent> planContentList = list(Wrappers.<MaintainPlanContent>lambdaQuery()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
			.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
			.in(MaintainPlanContent::getId, planUserDTO.getPlanIds())
			.ge(MaintainPlanContent::getPlanDate, DateUtil.today()));
		List<Long> planIds = planContentList.stream().map(t -> t.getId()).collect(Collectors.toList());
		if (Func.isEmpty(planIds)) {
			throw new ServiceException("操作失败,数据不存在!");
		}
		List<MaintainPlanContentUser> planContentUserList = planContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().in(MaintainPlanContentUser::getPlanId, planIds));
		// 根据工单id分组
		Map<Long, List<MaintainPlanContentUser>> planUserMap = planContentUserList.stream().collect(Collectors.groupingBy(t -> t.getPlanId()));

		List<MaintainPlanContentUser> batchSaveList = new ArrayList<>();
		List<Long> batchDelList = new ArrayList<>();
		List<MaintainPlanBaseInfoVO> delErrorList = new ArrayList<>();
		switch (planUserDTO.getType()) {
			case 0: {
				// 新增
				for (Long planId : planIds) {
					List<Long> userIds = ObjectUtil.cloneByStream(planUserDTO.getUserIds());
					if (planUserMap.containsKey(planId)) {
						// 原计划执行人userIds
						List<Long> itemUserIds = planUserMap.get(planId).stream().map(t -> t.getUserId()).collect(Collectors.toList());
						// 做差集
						List<Long> addUserIds = userIds.stream().filter(t -> !itemUserIds.contains(t)).collect(Collectors.toList());
						userIds = addUserIds;
					}

					for (Long addUserId : userIds) {
						MaintainPlanContentUser maintainPlanContentUser = new MaintainPlanContentUser();
						maintainPlanContentUser.setPlanId(planId);
						maintainPlanContentUser.setUserId(addUserId);
						batchSaveList.add(maintainPlanContentUser);
					}
				}
				break;
			}
			case 1: {
				// 替换 已选中的人员，替换覆盖全部旧人员数据
				batchDelList.addAll(planContentUserList.stream().map(t -> t.getId()).collect(Collectors.toList()));
				for (Long planId : planIds) {
					for (Long addUserId : planUserDTO.getUserIds()) {
						MaintainPlanContentUser maintainPlanContentUser = new MaintainPlanContentUser();
						maintainPlanContentUser.setPlanId(planId);
						maintainPlanContentUser.setUserId(addUserId);
						batchSaveList.add(maintainPlanContentUser);
					}
				}
				break;
			}
			case 2: {
				// 转移
				if (Func.isNull(planUserDTO.getOldUserId())) {
					throw new ServiceException("非法参数!");
				}
				// 设置转移对象与接收对象后，系统将把被转移者的业务数据，替换成接收者的业务数据。
				for (Map.Entry<Long, List<MaintainPlanContentUser>> item : planUserMap.entrySet()) {
					List<MaintainPlanContentUser> itemList = item.getValue();
					List<MaintainPlanContentUser> delPlanUsers = itemList.stream().filter(t -> t.getUserId().equals(planUserDTO.getOldUserId())).collect(Collectors.toList());
					if (Func.isNotEmpty(delPlanUsers)) {
						batchDelList.addAll(delPlanUsers.stream().map(t -> t.getId()).collect(Collectors.toList()));
						for (Long addUserId : planUserDTO.getUserIds()) {
							if (itemList.stream().anyMatch(t -> t.getUserId().equals(addUserId))) {
								// 已存在相同的人
								continue;
							}
							MaintainPlanContentUser maintainPlanContentUser = new MaintainPlanContentUser();
							maintainPlanContentUser.setPlanId(item.getKey());
							maintainPlanContentUser.setUserId(addUserId);
							batchSaveList.add(maintainPlanContentUser);
						}
					}
				}
				break;
			}
			case 3: {
				// 部分删除
				for (Map.Entry<Long, List<MaintainPlanContentUser>> item : planUserMap.entrySet()) {
					List<MaintainPlanContentUser> itemList = item.getValue();
					List<MaintainPlanContentUser> delPlanUsers = itemList.stream().filter(t -> planUserDTO.getUserIds().contains(t.getUserId())).collect(Collectors.toList());
					if (Func.isNotEmpty(delPlanUsers)) {
						if (itemList.size() - delPlanUsers.size() == 0) {
							// 存在删除后，计划执行人为0人情况
							List<Long> delPlanIds = delPlanUsers.stream().map(t -> t.getPlanId()).collect(Collectors.toList());
							List<MaintainPlanBaseInfoVO> errorList = baseMapper.getMaintainPlanBaseInfosByPlanIds(delPlanIds);
							delErrorList.addAll(errorList);
						} else {
							batchDelList.addAll(delPlanUsers.stream().map(t -> t.getId()).collect(Collectors.toList()));
						}
					}
				}
				break;
			}
		}

		boolean result = (batchDelList.size() > 0 ? planContentUserService.deleteLogic(batchDelList) : true)
			&& (batchSaveList.size() > 0 ? planContentUserService.saveBatch(batchSaveList) : true);

		// 将出错数据存到redis
		if (result && planUserDTO.getType() == 3 && delErrorList.size() > 0) {
			bladeRedis.setEx(AuthUtil.getUserId().toString().concat(StringPool.COLON).concat(PLAN_BATCH_EXCEL_ERR_CACHE), delErrorList, 30000L);
		}

		if (!result) {
			throw new ServiceException("数据异常,请重试!");
		}
		return delErrorList.size();
	}

	@Override
	public void downBatchDelUserErrExcel(HttpServletResponse response) throws IOException {
		Object data = bladeRedis.get(AuthUtil.getUserId().toString().concat(StringPool.COLON).concat(PLAN_BATCH_EXCEL_ERR_CACHE));
		if (Func.isEmpty(data)) {
			throw new ServiceException("未有错误数据导出！");
		}
		List<MaintainPlanBaseInfoVO> errorList = (List<MaintainPlanBaseInfoVO>) data;
		// 排序
		LinkedList<MaintainPlanBaseInfoVO> errorLinkedList = errorList.stream().sorted(Comparator.comparing(MaintainPlanBaseInfoVO::getElevatorId).thenComparing(MaintainPlanBaseInfoVO::getPlanDate)).collect(Collectors.toCollection(LinkedList::new));


		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			LinkedList<List<String>> rows = new LinkedList<>();
			List<String> title = CollUtil.newArrayList("计划日期", "项目", "梯号", "保养类型", "计划执行人");
			// 封装导出数据
			for (MaintainPlanBaseInfoVO errorDate : errorLinkedList) {
				List<String> row = new ArrayList<>(8);
				row.add(errorDate.getPlanDate());
				row.add(errorDate.getBuildingName());
				row.add(errorDate.getElevatorAddress());
				row.add(errorDate.getTemplateName());
				row.add(errorDate.getUserName());
				rows.add(row);
			}
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.setColumnWidth(0, 30);
			excelWriter.setColumnWidth(1, 30);
			excelWriter.setColumnWidth(2, 30);
			excelWriter.setColumnWidth(3, 30);
			excelWriter.setColumnWidth(4, 30);

			// 单独设置头部
			excelWriter.writeHeadRow(title);
			String fileName = "删除人员失败排期计划_" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			CommonUtil.setResponseHeader(response, fileName);
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@Override
	public boolean BatchModifyPlanTemplate(BatchModifyPlanTemplateDTO planUserDTO) {
		List<MaintainPlanContent> planContentList = list(Wrappers.<MaintainPlanContent>lambdaQuery()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId())
			.eq(MaintainPlanContent::getStatus, TaskStatusEnum.BY_DISABLE.getStatus())
			.in(MaintainPlanContent::getId, planUserDTO.getPlanIds())
			.ge(MaintainPlanContent::getPlanDate, DateUtil.today()));
		List<Long> planIds = planContentList.stream().map(t -> t.getId()).collect(Collectors.toList());
		if (Func.isEmpty(planIds) || planUserDTO.getPlanIds().size() != planContentList.size()) {
			throw new ServiceException("状态已变更，请刷新页面!");
		}

		List<MaintainPlanTemplate> maintainPlanTemplateList = planTemplateService.list(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, planIds));
		if (Func.isEmpty(maintainPlanTemplateList)) {
			return true;
		}

		maintainPlanTemplateList.forEach(planTemplate -> planTemplate.setTemplateId(planUserDTO.getTemplateId()));
		return planTemplateService.updateBatchById(maintainPlanTemplateList);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean addPlan(AddPlanVO addPlanVO) {
		if (Func.isEmpty(addPlanVO.getElevatorIds()) || Func.isEmpty(addPlanVO.getTaskPlanId())) {
			return false;
		}
		// 校验电梯与模板年审类型是否一致
		this.elevatorPlanUseTypeCheck(addPlanVO.getElevatorIds(), addPlanVO.getTaskPlanId());

		List<MaintainPlanTemplate> batchPlanTemplateList = new ArrayList<>();
		List<MaintainPlanContentUser> batchPlanContentUserList = new ArrayList<>();
		for (Long elevatorId : addPlanVO.getElevatorIds()) {
			MaintainPlanContent planContent = new MaintainPlanContent();
			planContent.setElevatorId(elevatorId);
			planContent.setTypeStatus(TaskStatusEnum.BY_DISABLE.getTypeStatus());
			planContent.setPlanDate(addPlanVO.getDate());
			if (this.save(planContent)) {
				// 任务关联模板
				for (int i = 0; i < addPlanVO.getTemplateIds().size(); i++) {
					MaintainPlanTemplate maintainPlanTemplate = new MaintainPlanTemplate();
					maintainPlanTemplate.setPlanId(planContent.getId());
					maintainPlanTemplate.setTemplateId(addPlanVO.getTemplateIds().get(i));
					maintainPlanTemplate.setSort(i);
					batchPlanTemplateList.add(maintainPlanTemplate);
				}

				// 任务关联人员
				List<Long> userIds = Func.toLongList(addPlanVO.getUserIds());
				for (Long userId : userIds) {
					MaintainPlanContentUser planContentUser = new MaintainPlanContentUser();
					planContentUser.setPlanId(planContent.getId());
					planContentUser.setUserId(userId);
					batchPlanContentUserList.add(planContentUser);
				}

				// 任务扩展表
				MaintainPlanContentExt contentExt = new MaintainPlanContentExt();
				contentExt.setPlanId(planContent.getId());
				PlanConfigVo planConfig = maintainPlanConfigService.getPlanConfigVo(Arrays.asList(elevatorId)).get(0);
				contentExt.setAuditType(planConfig.getAuditType());
				maintainPlanContentExtService.save(contentExt);

				// 维保任务延期历史日志表
				MaintainPlanDeferHistory maintainPlanDeferHistory = this.handlePlanDeferHistory(planContent.getId(), planContent.getPlanDate(), elevatorId, planConfig);
				maintainPlanDeferHistoryService.save(maintainPlanDeferHistory);

				// 插入任务跟踪信息
				malfunctionLogService.insertWorkLog(planContent.getId(), WorkTrackStatusEnum.DISTRIBUTION_WORK.getTypeName(), planContent.getStatus());
			}
		}

		planTemplateService.saveBatch(batchPlanTemplateList);
		planContentUserService.saveBatch(batchPlanContentUserList);
		// 更新电梯保养计划策略
		elevatorPlanStrategyService.planStrategyUpdate(addPlanVO.getElevatorIds());
		return true;
	}

	@Override
	public List<MaintainPlanTableVO> getDeptMaintainPlan(Long deptId, String yearMonth) {
		List<MaintainPlanTableVO> planTableList = new ArrayList<>();
		List<DeptMaintainPlanVO> planList = baseMapper.getDeptMaintainPlan(deptId, yearMonth);
		Map<String, List<DeptMaintainPlanVO>> mapByName = planList.stream().collect(Collectors.groupingBy(t -> t.getUserName()));
		for (Map.Entry<String, List<DeptMaintainPlanVO>> map : mapByName.entrySet()) {
			MaintainPlanTableVO planTable = new MaintainPlanTableVO();
			planTable.setUserName(map.getKey());
			List<DeptMaintainPlanVO> mapValue = map.getValue().stream().filter(t -> t.getPlanId() != null).collect(Collectors.toList());
			if (Func.isNotEmpty(mapValue)) {
				long elevatorAmount = mapValue.stream().map(t -> t.getElevatorId()).distinct().count();
				planTable.setElevatorAmount(elevatorAmount);
				double planAmount = mapValue.stream().collect(Collectors.summingDouble(t -> NumberUtil.div(1d, t.getTaskUserTotal(), 2)));
				planTable.setPlanAmount(NumberUtil.round(planAmount, 2).doubleValue());
				planTable.setPlanList(mapValue.stream().collect(Collectors.groupingBy(t -> t.getPlanDate())));
			}
			planTableList.add(planTable);
		}
		return planTableList;
	}

	@Override
	public void taskTemplateCheck(String elevatorIds, Long taskPlanId) {
		List<MaintainTaskPlan> taskPlanList = maintainTaskPlanService.getTaskPlanByElevatorIds(Func.toLongList(elevatorIds), AuthUtil.getTenantId());
		MaintainTaskPlan newTaskPlan = maintainTaskPlanService.getByIdAndTenantId(taskPlanId, AuthUtil.getTenantId());
		if (Func.isEmpty(taskPlanList)) {
			return;
		}
		if (Func.isEmpty(newTaskPlan)) {
			throw new ServiceException("任务计划数据异常!");
		}
		List<MaintainPlanContent> planContentList = baseMapper.selectList(Wrappers.<MaintainPlanContent>lambdaQuery()
			.in(MaintainPlanContent::getElevatorId, Func.toLongList(elevatorIds))
			.ne(MaintainPlanContent::getStatus, TaskStatusEnum.BY_SIGN.getStatus())
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId()));
		// 判断年审/非年审有无变更
		boolean isSameAuditType = taskPlanList.stream().allMatch(t -> Func.equalsSafe(t.getAuditType(), newTaskPlan.getAuditType()));
		if (planContentList.size() > 0 && !isSameAuditType) {
			throw new ServiceException("最后一个工单还没有完成,不允许更改年审类型不一样的保养计划!");
		}
	}

	@Override
	public void elevatorPlanUseTypeCheck(List<Long> elevatorIds, Long taskPlanId) {
		List<ElevatorDetail> detailList = elevatorDetailService.list(Wrappers.<ElevatorDetail>lambdaQuery().in(ElevatorDetail::getElevatorId, elevatorIds));
		if (Func.isEmpty(detailList)) {
			return;
		}

		List<Integer> useTypeList = detailList.stream().map(t -> t.getUseType()).distinct().collect(Collectors.toList());
		if (useTypeList.size() > 1) {
			throw new ServiceException("不同年审类型无法进行批量排期!");
		}

		MaintainTaskPlan newTaskPlan = maintainTaskPlanService.getByIdAndTenantId(taskPlanId, AuthUtil.getTenantId());
		if (Func.isEmpty(newTaskPlan)) {
			throw new ServiceException("任务计划数据异常!");
		}

		if (!Func.equalsSafe(useTypeList.get(0) == 0 ? 2 : 1, newTaskPlan.getAuditType())) {
			throw new ServiceException("当前计划不适用于当前年检类型电梯，请选择与电梯年检类型相同的保养计划!");
		}
	}

	@Override
	public IPage<PlanResultVo> getV2Page(PlanSearchDTOV2 planSearchDTO) {

		final List<Dict> dictList = dictService.list(Wrappers.<Dict>lambdaQuery().eq(Dict::getCode, "fault_reason"));
		final Map<String, List<Dict>> dictMap = dictList.stream().collect(Collectors.groupingBy(Dict::getDictKey));
		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		IPage<PlanResultVo> page = Condition.getPage(query);
		List<PlanResultVo> v2Page = baseMapper.getV2Page(page, planSearchDTO);
		v2Page.forEach(o -> {
			if (!Func.isNull(o.getSourcePlanType())) {
				String sourcePlanTypeName = MaintainPlanContentEnum.getName(o.getSourcePlanType());
				o.setSourcePlanTypeName(sourcePlanTypeName);
			}
		});
		page.setRecords(v2Page);
		Map<Long, List<MaintainSign>> longListMap = new HashMap<>(20);
		List<PlanResultVo> records = page.getRecords();
		Map<Long, List<PlanResultVo>> listMap = records.stream().collect(Collectors.groupingBy(PlanResultVo::getId));

		List<PlanResultVo> planResultVos = records.stream().filter(o -> o.getStatus() != MaintainPlanContentStatusEnum.DISABLE.getId()).collect(Collectors.toList());
		if (planResultVos != null && planResultVos.size() > 0) {
			List<Long> ids = planResultVos.stream().map(PlanResultVo::getId).collect(Collectors.toList());
			List<MaintainSign> list = maintainSignService.list(Wrappers.<MaintainSign>lambdaQuery().in(MaintainSign::getPlanId, ids));
			Map<Long, List<MaintainSign>> longListMap2 = list.stream().collect(Collectors.groupingBy(MaintainSign::getPlanId));
			longListMap.putAll(longListMap2);
		}

		for (int i = 0; i < records.size(); i++) {
			PlanResultVo planResultVo = records.get(i);
			if (planResultVo.getStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus()) {
				String userNames = planResultVo.getUserNames();
				String realUserNames = planResultVo.getRealUserNames();
				planResultVo.setUserNames(realUserNames);
				planResultVo.setRealUserNames(userNames);
			}
			//处理评分状态
			if (planResultVo.getStatus() == MaintainPlanContentStatusEnum.FINISH.getId()) {
				if (Convert.toInt(planResultVo.getScoreStatus(), -1) != 1) {
					Integer hours = Convert.toInt(DateUtil.between(planResultVo.getEndTime(), new Date(), DateUnit.HOUR));
					if (hours > 24) {
						planResultVo.setScoreStatus(0);
					}
				}
			} else {
				planResultVo.setScoreStatus(-1);
			}
			//处理故障原因
			final String problemItem = planResultVo.getFaultReasonItem();
			List<String> list = new ArrayList<>(16);
			if (StrUtil.isNotEmpty(problemItem)) {
				final String[] split = problemItem.split(",");
				for (String str : split) {
					final List<Dict> dicts = dictMap.get(str);
					if (dicts != null && dicts.size() > 0) {
						final String dictValue = dicts.get(0).getDictValue();
						list.add(dictValue);
					}
				}
				if (list.size() > 0) {
					planResultVo.setProblemItemName(String.join(",", list));
				}
			}

			planResultVo.setStatusName(TaskStatusEnum.getTaskName(planResultVo.getTypeStatus(), planResultVo.getStatus()));
			if (planResultVo.getStatus() == MaintainPlanContentStatusEnum.DISABLE.getId()) {
				planResultVo.setBeginTime(null);
				planResultVo.setEndTime(null);
				continue;
			} else if (planResultVo.getStatus() == MaintainPlanContentStatusEnum.BEGIN_PROCESSED.getId() || planResultVo.getStatus() == MaintainPlanContentStatusEnum.sign.getId()) {
				planResultVo.setEndTime(null);
				continue;
			}


			List<Integer> statusList = new ArrayList<>(5);
			statusList.add(planResultVo.getStatus());
			if (listMap.size() > 0) {
				List<MaintainSign> maintainSigns = longListMap.get(planResultVo.getId());
				if (maintainSigns != null && maintainSigns.size() > 0) {
					MaintainSign maintainSign = maintainSigns.stream().min(Comparator.comparing(o -> o.getCreateTime().getTime())).get();
					planResultVo.setBeginTime(maintainSign.getCreateTime());
				}
			}
			planResultVo.setStatusList(statusList);

			if (planResultVo.getStatus() == TaskStatusEnum.WX_CLOSED.getStatus() || planResultVo.getStatus() == TaskStatusEnum.WX_TURN_DOWN.getStatus() || planResultVo.getStatus() == TaskStatusEnum.WX_CHECK_PASS_CLOSED.getStatus()) {
				planResultVo.setWxNumberDescs(null);
			}
		}
		return page;
	}

	@Override
	public List<PlanResultVo> getV2PageDownExcel(PlanSearchDTOV2 planSearchDTO) {
		final List<Dict> dictList = dictService.list(Wrappers.<Dict>lambdaQuery().eq(Dict::getCode, "fault_reason"));
		final Map<String, List<Dict>> dictMap = dictList.stream().collect(Collectors.groupingBy(Dict::getDictKey));
		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		List<PlanResultVo> records = baseMapper.getV2Page(null, planSearchDTO);
		// 新增类型
		records.forEach(
			r -> {
				if (!Func.isNull(r.getSourcePlanType())) {
					r.setSourcePlanTypeName(MaintainPlanContentEnum.getName(r.getSourcePlanType()));
				}
			}
		);
		Map<Long, List<MaintainSign>> longListMap = new HashMap<>(20);
		Map<Long, List<PlanResultVo>> listMap = records.stream().collect(Collectors.groupingBy(PlanResultVo::getId));

		List<PlanResultVo> planResultVos = records.stream().filter(o -> o.getStatus() != MaintainPlanContentStatusEnum.DISABLE.getId()).collect(Collectors.toList());
		if (planResultVos != null && planResultVos.size() > 0) {
			List<Long> ids = planResultVos.stream().map(PlanResultVo::getId).collect(Collectors.toList());
			List<MaintainSign> list = maintainSignService.list(Wrappers.<MaintainSign>lambdaQuery().in(MaintainSign::getPlanId, ids));
			Map<Long, List<MaintainSign>> longListMap2 = list.stream().collect(Collectors.groupingBy(MaintainSign::getPlanId));
			longListMap.putAll(longListMap2);
		}

		for (int i = 0; i < records.size(); i++) {
			PlanResultVo planResultVo = records.get(i);
			planResultVo.setStatusName(TaskStatusEnum.getTaskName(planResultVo.getTypeStatus(), planResultVo.getStatus()));
			if (planResultVo.getStatus() == MaintainPlanContentStatusEnum.DISABLE.getId()) {
				planResultVo.setBeginTime(null);
				planResultVo.setEndTime(null);
				continue;
			} else if (planResultVo.getStatus() == MaintainPlanContentStatusEnum.BEGIN_PROCESSED.getId() || planResultVo.getStatus() == MaintainPlanContentStatusEnum.sign.getId()) {
				planResultVo.setEndTime(null);
				continue;
			}
			List<Integer> statusList = new ArrayList<>(5);
			statusList.add(planResultVo.getStatus());
			if (listMap.size() > 0) {
				List<MaintainSign> maintainSigns = longListMap.get(planResultVo.getId());
				if (maintainSigns != null && maintainSigns.size() > 0) {
					MaintainSign maintainSign = maintainSigns.stream().min(Comparator.comparing(o -> o.getCreateTime().getTime())).get();
					planResultVo.setBeginTime(maintainSign.getCreateTime());
				}
			}
			planResultVo.setStatusList(statusList);

			//处理故障原因
			final String problemItem = planResultVo.getFaultReasonItem();
			List<String> list = new ArrayList<>(16);
			if (StrUtil.isNotEmpty(problemItem)) {
				final String[] split = problemItem.split(",");
				for (String str : split) {
					final List<Dict> dicts = dictMap.get(str);
					if (dicts != null && dicts.size() > 0) {
						final String dictValue = dicts.get(0).getDictValue();
						list.add(dictValue);
					}
				}
				if (list.size() > 0) {
					planResultVo.setProblemItemName(String.join(",", list));
				}
			}
		}

		return records;
	}

	@Override
	public MaintainPlanContentWorkDetailVo workDetail(Long planId) {
		MaintainPlanContent maintainPlanContent = this.getById(planId);

		if (maintainPlanContent == null) {
			throw new ServiceException("未找到相关任务信息！");
		}
		// 2024-04-24 新增维修计划详情新字段
		RepairPlanDetailVO repairPlanDetailVO = new RepairPlanDetailVO();
		repairPlanDetailVO.setPlanDate(maintainPlanContent.getPlanDate());
		repairPlanDetailVO.setFinishDate(maintainPlanContent.getPlanFinishDate());
		Integer sourcePlanType = maintainPlanContent.getSourcePlanType();

		// 关联id列表
		List<Long> relatedWorkOrderIds = getFromPlanIdByOwnId(planId);
		// 是否允许修改，没有关联时允许
		Boolean isAllowModify = Func.isEmpty(relatedWorkOrderIds);

		if (!Func.isNull(sourcePlanType)) {
			String sourcePlanTypeName = MaintainPlanContentEnum.getName(sourcePlanType);
			repairPlanDetailVO.setSourcePlanTypeName(sourcePlanTypeName);
		}
		repairPlanDetailVO.setSourcePlanType(sourcePlanType);

		MaintainPlanContentWorkDetailVo maintainPlanContentWorkDetailVo = new MaintainPlanContentWorkDetailVo();
		maintainPlanContentWorkDetailVo.setPlanDetailVO(repairPlanDetailVO);
		maintainPlanContentWorkDetailVo.setRelatedWorkOrderIds(relatedWorkOrderIds);
		maintainPlanContentWorkDetailVo.setIsAllowModify(isAllowModify);
		// 任务基础信息
		PlanContentWorkDetailCommonVo planContentWorkDetailCommonVo = this.baseMapper.workDetail(planId);
		// 人员信息
		List<PlanMaintainUserVO> maintainUserList = planContentUserService.selectWorkUsers(planId);
		maintainUserList = maintainUserList.stream().filter(e -> {
			Integer userSignCount = signService.count(Wrappers.<MaintainSign>lambdaQuery().eq(MaintainSign::getPlanId, e.getPlanId()).eq(MaintainSign::getUserId, e.getMaintainUserId()));
			e.setUserSignCount(userSignCount);
			if (maintainPlanContent.getStatus().equals(TaskStatusEnum.BY_FINISH.getStatus()) && maintainPlanContent.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_BY.getId())) {
				return userSignCount > 0;
			} else if (maintainPlanContent.getStatus().equals(TaskStatusEnum.WX_SIGN_FINISH.getStatus()) && maintainPlanContent.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_WX.getId())) {
				return userSignCount > 0;
			} else {
				return true;
			}
		}).collect(Collectors.toList());


		planContentWorkDetailCommonVo.setBuildingTypeName(DictCache.getValue(PROJECT_TYPE_CODE, planContentWorkDetailCommonVo.getBuildingType()));
		maintainPlanContentWorkDetailVo.setPlanContentWorkDetailCommonVo(planContentWorkDetailCommonVo);

		maintainPlanContentWorkDetailVo.setMaintainUserList(maintainUserList);

		WorkTrackVO workTrack = malfunctionLogService.selectWorkTracks(planId);
		maintainPlanContentWorkDetailVo.setWorkTrack(workTrack);

		//2023-05-08 新增逾期判断需求
		MaintainPlanContentExt maintainPlanContentExt = maintainPlanContentExtService.getOne(Wrappers.<MaintainPlanContentExt>lambdaQuery().eq(MaintainPlanContentExt::getPlanId, planId));

		//表示年审工单
		if (maintainPlanContent.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_BY.getId())) {
			Integer auditType = 2;
			if (maintainPlanContentExt.getAuditType().equals(auditType)) {
				if (maintainPlanContent.getStatus() == TaskStatusEnum.BY_MAINTAIN_SIGN.getStatus() || maintainPlanContent.getStatus() == TaskStatusEnum.BY_SIGN.getStatus()) {
					if (ObjectUtil.isNotEmpty(maintainPlanContentExt.getOverdueDate())) {
						Date overdueDate = maintainPlanContentExt.getOverdueDate();
						if (Date.from(maintainPlanContentExt.getMaintainSignatureTime().atZone(ZoneId.systemDefault()).toInstant()).getTime() > overdueDate.getTime()) {
							maintainPlanContentWorkDetailVo.setOverdueDays(Convert.toInt(DateUtil.betweenDay(Date.from(maintainPlanContentExt.getMaintainSignatureTime().atZone(ZoneId.systemDefault()).toInstant()), overdueDate, true), 0));
						}
					}
				} else {
					if (maintainPlanContent.getElevatorId() > 0) {
						ElevatorPlanStrategy elevatorPlanStrategy = elevatorPlanStrategyService.getByElevatorId(maintainPlanContent.getElevatorId());
						if (elevatorPlanStrategy != null) {
							Date overdueDate = elevatorPlanStrategy.getOverdueDate();
							if (!Func.isNull(maintainPlanContent.getPlanDate())) {
								if (maintainPlanContent.getPlanDate().getTime() <= System.currentTimeMillis() && overdueDate.getTime() < System.currentTimeMillis()) {
									maintainPlanContentWorkDetailVo.setOverdueDays(Convert.toInt(DateUtil.betweenDay(new Date(), overdueDate, true), 0));
								}
							}
						}
					}
				}
			}
		} else if (maintainPlanContent.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_WX.getId())) {
			String malfunctionType = maintainPlanContentExt.getMalfunctionType();
			if (StrUtil.isNotEmpty(malfunctionType)) {
				Map<Long, List<WxFaultCause>> wxFaultCauseMap = wxFaultCauseService.list().stream().collect(Collectors.groupingBy(WxFaultCause::getId));
				List<String> list = Func.toStrList(malfunctionType);
				List<String> names = new ArrayList<>(16);
				for (String id : list) {
					List<WxFaultCause> wxFaultCauses = wxFaultCauseMap.get(Convert.toLong(id));
					if (wxFaultCauses != null && wxFaultCauses.size() > 0) {
						names.add(wxFaultCauses.get(0).getFaultName());
					}
				}
				if (names.contains("其他")) {
					names.remove("其他");
					names.add("其他");
					maintainPlanContentExt.setMalfunctionTypeNames(String.join(",", names) + "-" + Convert.toStr(maintainPlanContentExt.getMalfunctionTypeDesc()));
				} else {
					maintainPlanContentExt.setMalfunctionTypeNames(String.join(",", names));
				}
			}
		}
		if (!Func.isNull(maintainPlanContent.getPlanDate())) {
			long workDays = DateUtil.betweenDay(maintainPlanContent.getPlanDate(), maintainPlanContentExt.getMaintainSignatureTime() == null ? new Date() : Date.from(maintainPlanContentExt.getMaintainSignatureTime().atZone(ZoneId.systemDefault()).toInstant()), true);
			maintainPlanContentWorkDetailVo.setWorkDays(Convert.toInt(workDays, 0));
		}
		maintainPlanContentWorkDetailVo.setWorkMinute(maintainPlanContentExt.getWorkMinute());

		maintainPlanContentWorkDetailVo.setAuditType(maintainPlanContentExt.getAuditType());
		//返回整个对象
		maintainPlanContentWorkDetailVo.setMaintainPlanContentExt(maintainPlanContentExt);
		return maintainPlanContentWorkDetailVo;
	}

	private List<Long> getFromPlanIdByOwnId(Long planId) {
		return baseMapper.getFromPlanIdByOwnId(planId);
	}

	@Override
	public MalRecordDetailVO getMalfunctionHistoryList(Long planId) {
		MaintainPlanContent planContent = this.getById(planId);
		if (planContent == null) {
			throw new ServiceException("未找到相关保养计划！");
		}

		MalRecordDetailVO malRecordDetailVO = new MalRecordDetailVO();
		MaintainMalfunctionWorkVO workVO = MaintainMalfunctionWorkWrapper.build()
			.entityVO(malfunctionWorkService.getOne(Wrappers.<MaintainMalfunctionWork>lambdaQuery()
				.eq(MaintainMalfunctionWork::getPlanId, planId)
				.eq(MaintainMalfunctionWork::getIsDeleted, 0)));
		String problemItem = workVO.getProblemItem();
		if (Func.isNotBlank(problemItem)) {
			List<String> problemDesc = new ArrayList<>();
			String[] item = problemItem.split(",");
			for (int i = 0; i < item.length; i++) {
				String dictValue = DictCache.getValue("fault_problem", item[i]);
				problemDesc.add(dictValue);
			}
			workVO.setProblemDesc(problemDesc.stream().collect(Collectors.joining(",")));
		}
		workVO.setPlanFrom(planContent.getPlanFrom());
		malRecordDetailVO.setMalfunctionWork(workVO);

		//维修记录
		List<MalFunctionHistoryVO> historyVOList = maintainMalfunctionHistoryMapper.getMalHistoryList(planId);
		malRecordDetailVO.setHistoryVOList(historyVOList);

		return malRecordDetailVO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Long savePlan(PlanContentSaveDto planContentSaveDto) {

		if (planContentSaveDto.getBuildingId() == null) {
			Long buildingId = elevatorService.getById(planContentSaveDto.getElevatorId()).getBuildingId();
			planContentSaveDto.setBuildingId(buildingId);
		}
		//计算维修到达时间
		BuildingExt buildingExt = buildingExtService.getOne(Wrappers.<BuildingExt>lambdaQuery().eq(BuildingExt::getBuildingId, planContentSaveDto.getBuildingId()));
		if (StrUtil.isNotEmpty(buildingExt.getMalfunctionWorkArrivalTime())) {
			//预计达到时间
			long timeMillis = System.currentTimeMillis();
			if (buildingExt.getMalfunctionWorkArrivalTime().contains("m")) {
				Long time = Convert.toLong(buildingExt.getMalfunctionWorkArrivalTime().replace("m", "")) * 60 * 1000;
				timeMillis = timeMillis + time;
				Date date2 = new Date(timeMillis);
				planContentSaveDto.setPlanArriveDate(date2);
			} else if (buildingExt.getMalfunctionWorkArrivalTime().contains("h")) {
				Long time = Convert.toLong(buildingExt.getMalfunctionWorkArrivalTime().replace("h", "")) * 60 * 60 * 1000;
				timeMillis = timeMillis + time;
				Date date2 = new Date(timeMillis);
				planContentSaveDto.setPlanArriveDate(date2);
			}

		}

		List<Long> maintainUserIds = planContentSaveDto.getMaintainUserList().stream().map(PlanMaintainUserVO::getMaintainUserId).collect(Collectors.toList());
		if (planContentSaveDto.getStatus() == null) {
			planContentSaveDto.setStatus(TaskStatusEnum.WX_PENDING.getStatus());
		}
		final String tenantId = Func.isBlank(planContentSaveDto.getTenantId()) ? AuthUtil.getTenantId() : planContentSaveDto.getTenantId();
		// 任务主表
		MaintainPlanContent planContent;
		DateTime beginDateTime = null;
		if (planContentSaveDto.getPlanId() == null) {
			// 当前工单id为空时，新建对象，新增
			planContent = new MaintainPlanContent();
			if (planContentSaveDto.getPlanDate() != null) {
				beginDateTime = DateUtil.parseDate(planContentSaveDto.getPlanDate());
				planContent.setPlanDate(beginDateTime);
			} else {
				beginDateTime = new DateTime(new Date());
				planContent.setPlanDate(beginDateTime);
			}
			// 2024-04-23 新增 finishDate 字段
			if (planContentSaveDto.getFinishDate() != null && StringUtil.isNotBlank(planContentSaveDto.getFinishDate())) {
				DateTime finishDateTime = DateUtil.parseDate(planContentSaveDto.getFinishDate());
				if (finishDateTime.isBefore(beginDateTime)) {
					throw new ServiceException("结束时间不能早于开始时间");
				}
				planContent.setPlanFinishDate(finishDateTime);
			}
			planContent.setElevatorId(planContentSaveDto.getElevatorId());
			planContent.setTypeStatus(planContentSaveDto.getTypeStatus());
			planContent.setStatus(planContentSaveDto.getStatus());
			planContent.setTlwJoinId(planContentSaveDto.getTlwJoinId());
			planContent.setTenantId(tenantId);
			// 2024-04-23 新增 sourcePlanType 字段
			planContent.setSourcePlanType(planContentSaveDto.getSourcePlanType());


			this.save(planContent);

			// 添加维修详情
			if (Func.isEmpty(planContentSaveDto.getWorks())) {
				throw new ServiceException("维修报障信息不可为空");
			}
			planContentSaveDto.getWorks().forEach(e -> {
				e.setPlanId(planContent.getId());
			});
			malfunctionWorkService.saveBatch(planContentSaveDto.getWorks());
			//绑定关系(暂时只有质检,维修,保养)
			if (planContentSaveDto.getFromPlanId() != null) {
				List<Long> mailIds = planContentSaveDto.getWorks().stream().map(MaintainMalfunctionWork::getMalHistoryId).collect(Collectors.toList());
				List<MaintainMalfunctionHistory> maintainMalfunctionHistories = maintainMalfunctionHistoryMapper.selectBatchIds(mailIds);
				for (MaintainMalfunctionHistory maintainMalfunctionHistory : maintainMalfunctionHistories) {
					maintainMalfunctionHistory.setChildPlanId(planContent.getId());
					maintainMalfunctionHistoryMapper.updateById(maintainMalfunctionHistory);
				}
				//保存关系
				MaintainPlanRelation maintainPlanRelation = new MaintainPlanRelation();
				maintainPlanRelation.setOwnPlanId(planContent.getId());
				maintainPlanRelation.setFromPlanId(planContentSaveDto.getFromPlanId());
				maintainPlanRelationService.save(maintainPlanRelation);

				//更新质检工单的子工单数量
				MaintainPlanContent maintainPlanContent = this.getById(planContentSaveDto.getFromPlanId());
				if (maintainPlanContent.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_CHECK.getId())) {
					MaintainPlanCheck maintainPlanCheck = maintainPlanCheckService.getOne(Wrappers.<MaintainPlanCheck>lambdaQuery().eq(MaintainPlanCheck::getPlanId, planContentSaveDto.getFromPlanId()));
					Integer childCounts = Convert.toInt(maintainPlanCheck.getChildCounts(), 0);
					maintainPlanCheck.setChildCounts(childCounts + 1);
					maintainPlanCheckService.updateById(maintainPlanCheck);
				}
			}
			MaintainPlanContentExt maintainPlanContentExt = new MaintainPlanContentExt();
			maintainPlanContentExt.setPlanId(planContent.getId());
			maintainPlanContentExt.setTenantId(tenantId);
			maintainPlanContentExt.setElevatorStop(planContentSaveDto.getIsStop());
			maintainPlanContentExt.setEmerProcType(planContentSaveDto.getEmerProcType());
			maintainPlanContentExt.setWorkOrderType(planContentSaveDto.getWorkOrderType());
			maintainPlanContentExt.setFaultCompany(tenantService.getOne(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenantId)).getTenantName());
			if (planContentSaveDto.getStatus() != TaskStatusEnum.WX_ALLOCATION.getStatus()) {
				maintainPlanContentExt.setApplyTsFlag(roleService.hasRole(maintainUserIds, BaseRoleConstant.TECHNICAL_SUPPORT) ? 1 : 0);
				maintainPlanContentExt.setApplyPeFlag(roleService.hasRole(maintainUserIds, BaseRoleConstant.EXPERT) ? 1 : 0);
			}

			// 维修工单事件等级
			if (Func.isNotEmpty(planContent.getElevatorId())) {
				ElevatorRepeatFaultConfig faultConfig = elevatorRepeatFaultConfigService.getFaultLevelByElevator(planContent.getElevatorId());
				if (Func.isNotEmpty(faultConfig)) {
					maintainPlanContentExt.setFaultLabel(faultConfig.getTypeDesc());
					maintainPlanContentExt.setFaultLevel(faultConfig.getLevel());
					maintainPlanContentExt.setFaultOvertime(faultConfig.getOvertimeDay());
				}
			}
			maintainPlanContentExtService.save(maintainPlanContentExt);

			//等于待分配插入日志
			if (planContent.getStatus() == TaskStatusEnum.WX_ALLOCATION.getStatus()) {
				malfunctionLogService.insertWorkLog(planContent.getId(), WorkTrackStatusEnum.MALFUNCTION.getTypeName() + "-" + "web端", MaintainPlanContentStatusEnum.DISABLE.getId());
			}
		} else {
			planContent = this.getById(planContentSaveDto.getPlanId());
			if (planContentSaveDto.getPlanDate() != null) {
				planContent.setPlanDate(DateUtil.parseDate(planContentSaveDto.getPlanDate()));
			} else {
				planContent.setPlanDate(new Date());
			}
			planContent.setStatus(TaskStatusEnum.WX_PENDING.getStatus());
			this.updateById(planContent);
		}

		if (planContentSaveDto.getStatus() != TaskStatusEnum.WX_ALLOCATION.getStatus()) {
			User user = userService.getById(AuthUtil.getUserId());
			ApproveBusinessFlow flowStrategy = ApproveBusinessStrategy.get(ApproveTypeEnum.MALFUNCTION_APPLY.getId());
			BusinessApproveVO businessApprove = new BusinessApproveVO();
			businessApprove.setName("维修报障");
			businessApprove.setType(ApproveTypeEnum.MALFUNCTION_APPLY.getId());
			businessApprove.setSubmitId(AuthUtil.getUserId());
			businessApprove.setSubmitName(AuthUtil.getUserName());
			businessApprove.setApproveStatus(ApproveStatusEnum.PASSED.getId());
			businessApprove.setFromType(Func.isBlank(planContentSaveDto.getFromType()) ? PlanContentFormTypeEnum.ADMIN.getName() : planContentSaveDto.getFromType());
			//添加审批信息,判断是否存在审批流程
			String approveName = user.getRealName();
			String approveIds = user.getId().toString();
			List<ApproveUserVO> approveUserList = new ArrayList<>(16);
			if (planContentSaveDto.getCheckUsers() != null && planContentSaveDto.getCheckUsers().size() > 0) {
				businessApprove.setApproveStatus(ApproveStatusEnum.PENDING.getId());
				List<String> userNames = planContentSaveDto.getCheckUsers().stream().map(User::getRealName).collect(Collectors.toList());
				approveName = StringUtil.join(userNames, ",");
				List<Long> userIds = planContentSaveDto.getCheckUsers().stream().map(User::getId).collect(Collectors.toList());
				approveIds = StringUtil.join(userIds, ",");


				for (User user2 : planContentSaveDto.getCheckUsers()) {
					ApproveUserVO approveUserVO = new ApproveUserVO();
					approveUserVO.setUserId(user2.getId());
					approveUserVO.setStatus(approveUserVO.getStatus());
					approveUserList.add(approveUserVO);
				}
			}

			businessApprove.setApproveName(approveName);
			businessApprove.setApproveIds(approveIds);
			businessApprove.setWorkOrderId(planContent.getId());
			businessApprove.setPhone(planContentSaveDto.getPhone());
			businessApprove.setAssignUsers(maintainUserIds);
			businessApprove.setApproveUserList(approveUserList);
			businessApprove.setTenantId(tenantId);
			flowStrategy.addApprove(businessApprove);
		}
		// 2024-05-27 新增飞书异步逻辑
		String activeProfile = System.getProperty("spring.profiles.active");
		if ("prod".equals(activeProfile)) {
			larkMaintenanceService.sendRepairLarkMessage(planContent.getId());
		} else {
			larkMaintenanceService.sendRepairLarkMessageCopy(planContent.getId());
		}

		// 发送停梯延时队列 一小时后
		if (Func.isNotEmpty(planContentSaveDto.getIsStop())) {
			if (planContentSaveDto.getIsStop().equals(ElevatorStatusEnum.STOP.getStatus())) {
				// 推送通知
				List<Integer> triggerConditions = new ArrayList<>();
				// 停梯1小时预警通知
//				triggerConditions.add(TriggerConditionEnum.CONDITION_6.getCode());
				// 停梯2小时预警通知
				triggerConditions.add(TriggerConditionEnum.CONDITION_7.getCode());
				// 停梯4小时预警通知
//				triggerConditions.add(TriggerConditionEnum.CONDITION_8.getCode());
				Map<String, Object> callback = new HashMap<>();
				callback.put("elevatorId", planContent.getElevatorId());
				callback.put("msgCode", TriggerConditionEnum.CONDITION_7.getCode());
				for (Integer triggerCondition : triggerConditions) {
					eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
						.businessType(BusinessType.REPAIR.getCode())
						.triggerNode(TriggerNodeEnum.CREATE.getCode())
						.triggerCondition(triggerCondition)
						.bizId(planContent.getId())
						.tenantId(AuthUtil.getTenantId())
						.callback(callback)
						.build()
					));
				}
			}
			Elevator elevator = elevatorService.getById(planContent.getElevatorId());
			if (ObjectUtil.isNotEmpty(elevator) && ObjectUtil.isNotEmpty(elevator.getIsStop())) {
				if (!elevator.getIsStop().equals(planContentSaveDto.getIsStop())) {
					ElevatorStopLog elevatorStopLog = new ElevatorStopLog();
					elevatorStopLog.setElevatorId(planContent.getElevatorId());
					elevatorStopLog.setInitStatus(elevator.getIsStop());
					elevatorStopLog.setSetStatus(planContentSaveDto.getIsStop());
					elevatorStopLog.setModule(String.valueOf(planContent.getId()));
					elevatorStopLog.setPosition(3);
					elevatorStopLogService.insertStopLog(elevatorStopLog);

					elevator.setIsStop(planContentSaveDto.getIsStop());
					elevatorService.updateById(elevator);
				}
			}
		}


		//2024-07-08 新增维修电梯故障次数统计
		Integer workOrderType = Convert.toInt(planContentSaveDto.getWorkOrderType(), -1);
		//2表示客户报障加重复故障
		if (workOrderType == 2) {
			List<ElevatorWxNumber> numbers = elevatorWxNumberService.list(Wrappers.<ElevatorWxNumber>lambdaQuery().ne(ElevatorWxNumber::getStatus, StatusEnum.DISABLE.getId()));
			if (numbers.size() > 0) {
				List<Integer> days = numbers.stream().map(ElevatorWxNumber::getDays).distinct().collect(Collectors.toList());
				Integer max = days.stream().reduce(days.get(0), Integer::max) * -1;
				List<MaintainPlanContent> contentList = getPlanListOfElevatorId(planContentSaveDto.getElevatorId(), DateUtil.offsetDay(new Date(), max), new Date());
				contentList = contentList.stream().filter(o -> !o.getId().equals(planContent.getId())).collect(Collectors.toList());
				for (ElevatorWxNumber elevatorWxNumber : numbers) {
					Date offsetDay = DateUtil.offsetDay(new Date(), elevatorWxNumber.getDays() * -1);
					Integer counts = 0;
					for (MaintainPlanContent maintainPlanContent : contentList) {
						if (maintainPlanContent.getCreateTime().after(offsetDay)) {
							counts = counts + 1;
						}
					}
					if (counts >= elevatorWxNumber.getRepetitionCounts()) {
						ElevatorWxNumberStatistic elevatorWxNumberStatistic = new ElevatorWxNumberStatistic();
						elevatorWxNumberStatistic.setElevatorId(planContentSaveDto.getElevatorId());
						elevatorWxNumberStatistic.setWxNumberId(elevatorWxNumber.getId());
						elevatorWxNumberStatistic.setWxNumberDesc(elevatorWxNumber.getTypeDesc());
						elevatorWxNumberStatistic.setPlanId(planContent.getId());
						elevatorWxNumberStatisticService.save(elevatorWxNumberStatistic);
					}
				}
			}
		}
		//2024-11-06 新增维修预警信息
		if (Func.isEmpty(planContentSaveDto.getCheckUsers())) {
			malfunctionWorkService.sendYjMsg(planContentSaveDto, planContent.getId());
		}

		List<MaintainPlanContentUser> list = planContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().eq(MaintainPlanContentUser::getPlanId, planContent.getId()));
		if (list.size() > 0) {
			list.forEach(o -> {
				if (planContentSaveDto.getPlanArriveDate() != null) {
					o.setPlanArriveDate(new Date(DateUtils.getNowDateEnd(planContentSaveDto.getPlanArriveDate())));
				}
			});
			planContentUserService.updateBatchById(list);
		}
		// 2025-07 新增告警记录关联id
		Long rescueNoticeId = planContentSaveDto.getRescueNoticeId();
		if(Func.isNotEmpty(rescueNoticeId)){
			RescueNotice currentNotice = rescueNoticeService.getById(rescueNoticeId);
			if(!currentNotice.getConfirmType().equals(RescueNoticeConfirmTypeEnum.NOT_CONFIRM.getConfirmType())){
				throw new ServiceException("报警已经确认，无需再确认");
			}
			currentNotice.setPlanId(planContent.getId());
			currentNotice.setConfirmType(RescueNoticeConfirmTypeEnum.CONFIRM_ACCURATE.getConfirmType());
			currentNotice.setRemark(planContentSaveDto.getRescueNoticeRemark());
			rescueNoticeService.updateById(currentNotice);
			// 给梯联网进行回调
			TicketFeedBackDTO feedBack = new TicketFeedBackDTO();
			feedBack.setDeviceId(currentNotice.getDeviceId());
			feedBack.setRescueId(currentNotice.getCallId());
			feedBack.setState(3);
			feedBack.setIsCreateRescue(false);
			tlwScreenService.ticketFeedback(feedBack);
		}
		return planContent.getId();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean tlwMalPlanSave(TlwMalfunctionPlanSaveDTO tlwMalfunctionPlanSave) {
		SimpleElevatorInfoVO simpleElevatorInfoVO = elevatorDeviceService.getSimpleElevatorInfo(tlwMalfunctionPlanSave.getDeviceId());
		if (Func.isEmpty(simpleElevatorInfoVO)) {
			throw new ServiceException("维修工单创建失败，系统未找到该电梯");
		}
		int checkSave = baseMapper.selectCount(Wrappers.<MaintainPlanContent>lambdaQuery().eq(MaintainPlanContent::getTlwJoinId, tlwMalfunctionPlanSave.getTlwJoinId()));
		if (checkSave > 0) {
			throw new ServiceException("维修工单创建失败，该报警已创建维修工单");
		}

		PlanContentSaveDto planContentSaveDto = new PlanContentSaveDto();
		planContentSaveDto.setElevatorId(simpleElevatorInfoVO.getElevatorId());
		planContentSaveDto.setTypeStatus(MaintainPlanContentEnum.MAINTAIN_WX.getId());
		planContentSaveDto.setPlanFrom(PlanContentFormTypeEnum.TLW.getId());
		planContentSaveDto.setIsStop(1);
		planContentSaveDto.setBuildingName(simpleElevatorInfoVO.getBuildingName());
		planContentSaveDto.setElevatorAddress(simpleElevatorInfoVO.getElevatorAddress());
		planContentSaveDto.setTlwJoinId(tlwMalfunctionPlanSave.getTlwJoinId());
		planContentSaveDto.setTenantId(simpleElevatorInfoVO.getTenantId());
		planContentSaveDto.setFromType(PlanContentFormTypeEnum.TLW.getName());
		planContentSaveDto.setEmerProcType(EmerProcTypeEnum.VERY_URGENT.getId());
		planContentSaveDto.setWorkOrderType(WorkOrderTypeEnum.CLIENT.getId());
		// 报障信息
		List<MaintainMalfunctionWork> works = new ArrayList<>(2);
		MaintainMalfunctionWork malfunctionWork = new MaintainMalfunctionWork();
		malfunctionWork.setMfImgUrl(tlwMalfunctionPlanSave.getMfImgUrl());
		malfunctionWork.setProblemItem("12");
		malfunctionWork.setProblemOtherDesc(tlwMalfunctionPlanSave.getProblemOtherDesc());
		malfunctionWork.setName(tlwMalfunctionPlanSave.getName());
		malfunctionWork.setPhone(tlwMalfunctionPlanSave.getPhone());
		malfunctionWork.setPlanFrom(PlanContentFormTypeEnum.TLW.getId());
		malfunctionWork.setTenantId(simpleElevatorInfoVO.getTenantId());
		works.add(malfunctionWork);
		planContentSaveDto.setWorks(works);

		List<Long> elevatorIds = new ArrayList<>(16);
		elevatorIds.add(simpleElevatorInfoVO.getElevatorId());
		List<UserInfoVo> userInfoList = maintainMalfunctionRescueService.getUserList(elevatorIds, 1, null, null);
		if (Func.isEmpty(userInfoList)) {
			throw new ServiceException("维修工单创建失败，项目电梯未设置维保负责人");
		}
		List<PlanMaintainUserVO> maintainUserList = new ArrayList<>(4);
		List<User> checkUsers = new ArrayList<>(4);
		userInfoList.forEach(t -> {
			PlanMaintainUserVO planMaintainUserVO = new PlanMaintainUserVO();
			planMaintainUserVO.setMaintainUserId(t.getUserId());
			planMaintainUserVO.setMaintainUserPhone(t.getPhone());
			maintainUserList.add(planMaintainUserVO);

			User user = new User();
			user.setId(t.getUserId());
			user.setRealName(t.getUserName());
			checkUsers.add(user);
		});
		planContentSaveDto.setMaintainUserList(maintainUserList);
		planContentSaveDto.setCheckUsers(checkUsers);
		this.savePlan(planContentSaveDto);
		return Boolean.TRUE;
	}

	@Override
	public List<TlwMalfunctioningListVO> tlwMalfunctioningList(String deviceId) {
		SimpleElevatorInfoVO simpleElevatorInfoVO = elevatorDeviceService.getSimpleElevatorInfo(deviceId);
		if (Func.isEmpty(simpleElevatorInfoVO)) {
			throw new ServiceException("查询失败，系统未找到该电梯");
		}

		// 获取正在维修列表
		List<TlwMalfunctioningListVO> malfunctioningList = baseMapper.selectMalfunctioningByElevatorIdList(simpleElevatorInfoVO.getElevatorId());
		for (TlwMalfunctioningListVO malfunctioning : malfunctioningList) {
			if (StringUtil.isNotBlank(malfunctioning.getProblemItem())) {
				List<String> problemKeys = Func.toStrList(malfunctioning.getProblemItem());
				List<String> problemNames = new ArrayList<>();
				for (String problemKey : problemKeys) {
					String problemName = DictCache.getValue("fault_problem", problemKey);
					problemNames.add(problemName);
				}
				malfunctioning.setProblemItemNames(problemNames);
			}
		}
		return malfunctioningList;
	}

	@Override
	public TlwMalfunctionDetailVO tlwMalfunctionDetail(String tlwJoinId) {
		return baseMapper.tlwMalfunctionDetail(tlwJoinId);
	}


	@Override
	public IPage<PlanListVo> malfunctionList(PlanSearchDTO planSearchDTO) {
		if (planSearchDTO.getUserIds() != null && planSearchDTO.getUserIds().size() > 0) {
			planSearchDTO.setGroupIds(null);
		}
		if (planSearchDTO.getElevatorIds() != null && planSearchDTO.getElevatorIds().size() > 0) {
			planSearchDTO.setBuildingIds(null);
		}
		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		IPage<PlanListVo> page = Condition.getPage(query);
		List<PlanListVo> planListVos = baseMapper.malfunctionList(planSearchDTO.getSize() == 0 ? null : page, planSearchDTO);
		page.setRecords(planListVos);
		return page;
	}

	@Override
	public LastPlanVO selectElevatorLastPlan(Long elevatorId, Long planId) {
		return baseMapper.selectElevatorLastPlan(elevatorId, planId);
	}

	@Override
	public List<LastPlanVO> selectElevatorLastPlans(List<Long> elevatorIds, List<Long> planIds) {
		return baseMapper.selectElevatorLastPlans(elevatorIds, planIds);
	}

	@Override
	public String getPlanTemplateName(Long planId) {
		return baseMapper.getPlanTemplateName(planId);
	}

	@Override
	public List<PlanTemplateDescVO> getPlanTemplateNames(List<Long> planIds) {
		return baseMapper.getPlanTemplateNames(planIds);
	}

	/**
	 * 更新电梯日统计表故障次数
	 *
	 * @param elevatorId 电梯id
	 * @param tenantId   租户id
	 */
	public void updateElevatorDayMalFunction(Long elevatorId, String tenantId) {
		Elevator elevator = elevatorService.getOne(Wrappers.<Elevator>lambdaQuery().eq(Elevator::getId, elevatorId).eq(Elevator::getTenantId, tenantId));
		if (elevator == null) {
			return;
		}

		//更新电梯日统计表故障次数
		ElevatorMalfunctionDay malfunctionDay = elevatorMalfunctionDayService.selectElevatorDay(elevator.getId(), DateUtil.format(new Date(), "yyyy-MM-dd"), tenantId);
		if (null == malfunctionDay) {
			malfunctionDay = new ElevatorMalfunctionDay();
		}
		int malfunctionCounts = malfunctionDay.getMalfuntionCounts() == null ? 0 : malfunctionDay.getMalfuntionCounts();
		malfunctionCounts += 1;
		malfunctionDay.setMalfuntionCounts(malfunctionCounts);
		malfunctionDay.setElevatorId(elevator.getId());
		malfunctionDay.setUnitId(Long.getLong(elevator.getUnit()));
		malfunctionDay.setBlockId(Long.getLong(elevator.getBlock()));
		malfunctionDay.setBuildingId(elevator.getBuildingId());
		malfunctionDay.setTenantId(tenantId);
		elevatorMalfunctionDayService.saveOrUpdate(malfunctionDay);
	}

	/**
	 * 封装微信模板消息并发送
	 *
	 * @param kw1
	 * @param kw2
	 * @param kw3
	 * @param kw4
	 * @param desc
	 * @param userId
	 */
	public void sendWechatMsg(String title, String kw1, String kw2, String kw3, String kw4, String desc, Long userId) {

		UserWeixin userWeixin = userWeixinService.getOne(new LambdaQueryWrapper<UserWeixin>().eq(UserWeixin::getUserId, userId));
		if (ObjectUtil.isEmpty(userWeixin) || ObjectUtil.isEmpty(userWeixin.getOpenid())) {
			return;
		}

		// 封装模板数据
		MessageTemplateVO messageTemplateVO = new MessageTemplateVO();
		MiniprogramVO miniprogramVO = new MiniprogramVO();
		miniprogramVO.setAppid(APP_ID);
		miniprogramVO.setPagepath(PAGE_PATH);
		MessageVO messageVO = new MessageVO();
		CommonVO first = new CommonVO(title, null);
		CommonVO keyword1 = new CommonVO(kw1, null);
		CommonVO keyword2 = new CommonVO(kw2, null);
		CommonVO keyword3 = new CommonVO(kw3, null);
		CommonVO keyword4 = new CommonVO(kw4, null);
		CommonVO remark = new CommonVO(desc, null);
		messageVO.setFirst(first);
		messageVO.setKeyword1(keyword1);
		messageVO.setKeyword2(keyword2);
		messageVO.setKeyword3(keyword3);
		messageVO.setKeyword4(keyword4);
		messageVO.setRemark(remark);
		messageTemplateVO.setData(messageVO);
		messageTemplateVO.setTouser(userWeixin.getOpenid());
		messageTemplateVO.setMiniprogram(miniprogramVO);
		String tmp = weiXinUtil.sendTemplateMessage(JSONObject.toJSONString(messageTemplateVO));
		log.info("WeiXinUtil ===>>> " + tmp);
	}

	@Override
	public ElevatorSimpleInfoVO getElevatorSimpleInfo(Long planId) {
		return baseMapper.getElevatorSimpleInfo(planId);
	}

	@Override
	public List<MaintainPlanContent> listPlanForCount(Integer typeStatus, String startTime, String endTime) {
		return baseMapper.listPlanForCount(typeStatus, startTime, endTime);
	}

	@Override
	public List<MaintainPlanContent> listWxPlanForCount(String typeStatus) {
		return baseMapper.listWxPlanForCount(typeStatus);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean byScoreSubmit(MaintainPlanContentExt maintainPlanContentExt) {
		MaintainPlanContent maintainPlanContent = this.getById(maintainPlanContentExt.getPlanId());
		if (maintainPlanContent == null) {
			throw new ServiceException("评分工单不存在");
		}
		MaintainPlanContentExt maintainExt = maintainPlanContentExtService.getOne(Wrappers.<MaintainPlanContentExt>lambdaQuery().eq(MaintainPlanContentExt::getPlanId, maintainPlanContentExt.getPlanId()));
		if (maintainPlanContent.getStatus() != TaskStatusEnum.BY_SIGN.getStatus() || maintainExt == null) {
			throw new ServiceException("此工单未开始评分");
		}

		BladeUser bladeUser = AuthUtil.getUser();
		maintainExt.setScore(maintainPlanContentExt.getScore());
		maintainExt.setScoreStatus(1);
		maintainExt.setScoreRemark(maintainPlanContentExt.getScoreRemark());
		maintainExt.setScoreUserId(bladeUser.getUserId());
		maintainExt.setScoreUserName(bladeUser.getNickName());
		if (maintainPlanContentExtService.updateById(maintainExt)) {
			List<MaintainPlanScoreRecord> scoreRecordList = maintainPlanScoreRecordService.list(Wrappers.<MaintainPlanScoreRecord>lambdaQuery().eq(MaintainPlanScoreRecord::getPlanId, maintainPlanContentExt.getPlanId()));
			if (scoreRecordList.size() > 0) {
				// 更新评分记录
				scoreRecordList.forEach(scoreRecord -> {
					scoreRecord.setScore(maintainPlanContentExt.getScore());
				});
				maintainPlanScoreRecordService.updateBatchById(scoreRecordList);
			} else {
				// 插入评分记录
				List<MaintainSign> maintainSignList = maintainSignService.getMaintainSignList(Arrays.asList(maintainPlanContent.getId()));
				if (Func.isNotEmpty(maintainSignList)) {
					Map<Long, List<MaintainSign>> signMap = maintainSignList.stream().collect(Collectors.groupingBy(t -> t.getUserId()));
					signMap.forEach((k, v) -> {
						MaintainSign maintainSign = v.get(0);
						MaintainPlanScoreRecord maintainPlanScoreRecord = new MaintainPlanScoreRecord();
						maintainPlanScoreRecord.setPlanId(maintainPlanContent.getId());
						maintainPlanScoreRecord.setElevatorId(maintainPlanContent.getElevatorId());
						maintainPlanScoreRecord.setUserId(k);
						maintainPlanScoreRecord.setUserDeptIds(maintainSign.getDeptIds());
						maintainPlanScoreRecord.setUserRoleIds(maintainSign.getRoleIds());
						maintainPlanScoreRecord.setScore(maintainPlanContentExt.getScore());
						maintainPlanScoreRecord.setOrderTime(maintainExt.getPlanBeginTime());
						maintainPlanScoreRecord.setFinishTime(maintainExt.getPlanEndTime());
						scoreRecordList.add(maintainPlanScoreRecord);
					});
					maintainPlanScoreRecordService.saveBatch(scoreRecordList);
				}
			}
			return true;
		}

		return false;
	}

	@Override
	public List<Map<String, String>> getSugarCheckPlanStatus(List<Long> deptIds) {

		String[] labelArr = {"未执行", "未完成", "已完成"};
		List<Map<String, String>> result = new ArrayList<>(3);
		SugarCheckPlanStatusVO entity = new SugarCheckPlanStatusVO();
		List<Long> allDeptIds = deptService.selectDeptAndAllChildIds(deptIds);
		List<ElevatorPrincipalDept> elevatorList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,allDeptIds));
		if(Func.isNotEmpty(elevatorList)){
			List<Long> elevatorIds = elevatorList.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
			entity = baseMapper.getSugarCheckPlanStatus(elevatorIds);
		}

		Map<String, String> toStart = new HashMap<>(3);
		toStart.put("label", labelArr[0]);
		toStart.put("value", String.format("%d单", entity.getToStart()));
		if (entity.getTotal() > 0) {
			toStart.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(entity.getToStart(), entity.getTotal()), 100)));
		} else {
			toStart.put("per", "0.0%");
		}
		result.add(toStart);

		Map<String, String> unfinished = new HashMap<>(3);
		unfinished.put("label", labelArr[1]);
		unfinished.put("value", String.format("%d单", entity.getUnfinished()));
		if (entity.getTotal() > 0) {
			unfinished.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(entity.getUnfinished(), entity.getTotal()), 100)));
		} else {
			unfinished.put("per", "0.0%");
		}
		result.add(unfinished);

		Map<String, String> finished = new HashMap<>(3);
		finished.put("label", labelArr[2]);
		finished.put("value", String.format("%d单", entity.getFinished()));
		if (entity.getTotal() > 0) {
			finished.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(entity.getFinished(), entity.getTotal()), 100)));
		} else {
			finished.put("per", "0.0%");
		}
		result.add(finished);

		return result;
	}

	@Override
	public List<SugarCheckPlanHalfYearVO> getSugarCheckPlanHalfYear() {

		String[] typeArr = {"未执行", "未完成", "已完成", "及时", "逾期"};
//		List<Date> dateArr = getDatesBetweenByMonth(localDate(DateUtil.offsetMonth(DateUtil.date(),-6)),localDate(DateUtil.offsetMonth(DateUtil.date(),-1)));
		List<Date> dateArr = getDatesBetweenByMonth(localDate(DateUtil.offsetMonth(DateUtil.date(), -5)), localDate(DateUtil.date()));
		// 初始化数据
		List<SugarCheckPlanHalfYearVO> initResult = initResult(typeArr, dateArr);
		List<SugarCheckPlanStatusVO> entityList = baseMapper.getSugarCheckPlanHalfYear(DateUtil.format(dateArr.get(0), "yyyy-MM"), DateUtil.format(dateArr.get(dateArr.size() - 1), "yyyy-MM"));
		if (ObjectUtil.isNotEmpty(entityList)) {
			initResult.get(0).getDataList().forEach(e -> {
				entityList.forEach(j -> {
					if (String.valueOf(e.get("label")).equals(j.getPlanDate())) {
						e.put("value", j.getToStart());
						e.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(j.getToStart(), j.getTotal()), 100)));
					}
				});
				e.put("label", DateUtil.format(DateUtil.parse(String.valueOf(e.get("label")), "yyyy-MM"), "M月"));
			});
			initResult.get(1).getDataList().forEach(e -> {
				entityList.forEach(j -> {
					if (String.valueOf(e.get("label")).equals(j.getPlanDate())) {
						e.put("value", j.getUnfinished());
						e.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(j.getUnfinished(), j.getTotal()), 100)));
					}
				});
				e.put("label", DateUtil.format(DateUtil.parse(String.valueOf(e.get("label")), "yyyy-MM"), "M月"));
			});
			initResult.get(2).getDataList().forEach(e -> {
				entityList.forEach(j -> {
					if (String.valueOf(e.get("label")).equals(j.getPlanDate())) {
						e.put("value", j.getFinished());
						e.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(j.getFinished(), j.getTotal()), 100)));
					}
				});
				e.put("label", DateUtil.format(DateUtil.parse(String.valueOf(e.get("label")), "yyyy-MM"), "M月"));
			});
			initResult.get(3).getDataList().forEach(e -> {
				entityList.forEach(j -> {
					if (String.valueOf(e.get("label")).equals(j.getPlanDate())) {
						e.put("value", j.getInTime());
						e.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(j.getInTime(), j.getTotal()), 100)));
					}
				});
				e.put("label", DateUtil.format(DateUtil.parse(String.valueOf(e.get("label")), "yyyy-MM"), "M月"));
			});
			initResult.get(4).getDataList().forEach(e -> {
				entityList.forEach(j -> {
					if (String.valueOf(e.get("label")).equals(j.getPlanDate())) {
						e.put("value", j.getOverdue());
						e.put("per", String.format("%.1f%%", NumberUtil.mul(NumberUtil.div(j.getOverdue(), j.getTotal()), 100)));
					}
				});
				e.put("label", DateUtil.format(DateUtil.parse(String.valueOf(e.get("label")), "yyyy-MM"), "M月"));
			});
		}

		return initResult;
	}

	@Override
	public List<MaintainTypeDataVO> getSugarCheckPlanOneMonth() {
		List<MaintainTypeDataVO> dataList = new ArrayList<>();
		List<MaintainPlanContentVO> list = baseMapper.selectMaintainListByOneMonth();
		long orderSum = list.stream().count();
		for (int i = 0; i < 5; i++) {
			MaintainTypeDataVO maintainTypeDataVO = new MaintainTypeDataVO();
			maintainTypeDataVO.setType(i);
			long orderTotal = 0;
			BigDecimal percentage = BigDecimal.ZERO;
			if (orderSum > 0) {
				switch (i) {
					case 0: {
						// 未执行
						orderTotal = list.stream().filter(t -> t.getStatus() == TaskStatusEnum.BY_DISABLE.getStatus()).count();
						break;
					}
					case 1: {
						// 未完成
						orderTotal = list.stream().filter(t -> t.getStatus() == TaskStatusEnum.BY_PROCESSED.getStatus() || t.getStatus() == TaskStatusEnum.BY_FINISH.getStatus()).count();
						break;
					}
					case 2: {
						// 已完成
						orderTotal = list.stream().filter(t -> t.getStatus() == TaskStatusEnum.BY_SIGN.getStatus()).count();
						break;
					}
					case 3: {
						// 及时
						orderTotal = list.stream().filter(t -> t.getOverdueDay() == null).count();
						break;
					}
					case 4: {
						// 逾期
						orderTotal = list.stream().filter(t -> t.getOverdueDay() != null && t.getOverdueDay() == 1).count();
						break;
					}
				}
				percentage = new BigDecimal(orderTotal).divide(new BigDecimal(orderSum), 3, BigDecimal.ROUND_HALF_UP);
			}
			maintainTypeDataVO.setOrderTotal(orderTotal);
			maintainTypeDataVO.setPercentage(percentage);
			dataList.add(maintainTypeDataVO);
		}

		return dataList;
	}

	@Override
	public List<SugarCheckPlanDeptRankingsVO> getSugarCheckPlanDeptRankings(PlanCheckStatisticsQueryDTO queryDTO) {

		// 组内
		List<Dept> deptList = deptService.selectDeptAndAllChildDepts(queryDTO.getDeptIds());
		List<SugarCheckPlanDeptRankingsVO> resultList = deptList.stream().map(e -> {
			SugarCheckPlanDeptRankingsVO result = baseMapper.getSugarCheckPlanDeptRankings(e.getId(), queryDTO.getStartDate(), queryDTO.getEndDate());
			if (ObjectUtil.isEmpty(result)) {
				result = new SugarCheckPlanDeptRankingsVO();
				result.setPlanCount(0);
				result.setInTimePerStr("0.0%");
				result.setFinishedPerStr("0.0%");
			}
			result.setDeptId(e.getId());
			result.setDeptName(e.getDeptName());
			result.setInTimePerStr(result.getInTimePer() + "%");
			result.setFinishedPerStr(result.getFinishedPer() + "%");
			return result;
		}).collect(Collectors.toList());
		resultList = resultList.stream().filter(e -> e.getPlanCount() > 0).sorted(Comparator.comparing(SugarCheckPlanDeptRankingsVO::getInTimePer).reversed()
			.thenComparing(SugarCheckPlanDeptRankingsVO::getFinishedPer).reversed()
			.thenComparing(SugarCheckPlanDeptRankingsVO::getPlanCount).reversed()).collect(Collectors.toList());
		return resultList;
	}

	private List<SugarCheckPlanHalfYearVO> initResult(String[] typeArr, List<Date> dateArr) {
		List<SugarCheckPlanHalfYearVO> initResult = new ArrayList<>(typeArr.length);
		for (String type : typeArr) {
			SugarCheckPlanHalfYearVO typeMap = new SugarCheckPlanHalfYearVO();
			typeMap.setType(type);
			List<Map<String, Object>> dataList = dateArr.stream().map(e -> {
				Map<String, Object> dataMap = new HashMap<>(3);
				dataMap.put("label", DateUtil.format(e, "yyyy-MM"));
				dataMap.put("value", 0);
				dataMap.put("per", "0.0%");
				return dataMap;
			}).collect(Collectors.toList());
			typeMap.setDataList(dataList);
			initResult.add(typeMap);
		}
		return initResult;
	}

	@Override
	public MalfunctionScreenVO detailData(AnalysisStatisticalDTO param) {
		// 最近7天的维修工单
		List<Long> allDeptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,allDeptIds));
		List<MaintainPlanContentVO> tasks = new ArrayList<>();
		List<ElevatorEffectedVO> elevatorEffectedList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorList)){
			List<Long> elevatorIds = elevatorList.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
			tasks = baseMapper.selectMalfunctionListByDate(param.getBeginDate(), param.getEndDate());
			tasks = tasks.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
			elevatorEffectedList = baseMapper.selectElevatorEffectedListByDate(param.getBeginDate(), param.getEndDate());
			elevatorEffectedList = elevatorEffectedList.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
		}
		return handleMalfunctionScreenDataBetweenDay(tasks, elevatorEffectedList, DateUtil.betweenDay(param.getBeginDate(), DateUtil.offsetDay(param.getEndDate(),1), Boolean.TRUE));
	}

	@Override
	public List<MalfunctionScreenVO> repairProject(AnalysisStatisticalDTO param) {
		List<MalfunctionScreenVO> resultList = new ArrayList<>();
		// 最近7天的维修工单
		List<Long> allDeptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,allDeptIds));
		List<MaintainPlanContentVO> tasks = new ArrayList<>();
		List<ElevatorEffectedVO> elevatorEffectedList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorList)){
			List<Long> elevatorIds = elevatorList.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
			tasks = baseMapper.selectMalfunctionListByDate(param.getBeginDate(), param.getEndDate());
			tasks = tasks.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
			elevatorEffectedList = baseMapper.selectElevatorEffectedListByDate(param.getBeginDate(), param.getEndDate());
			elevatorEffectedList = elevatorEffectedList.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
		}
//		if (Func.isNotEmpty(tasks)) {
//			tasks = tasks.stream().filter(e -> e.getWorkOrderType().equals(WorkOrderTypeEnum.CLIENT.getId())).collect(Collectors.toList());
//		}
		Map<Long, String> nameMap;
		// 根据项目名称分组
		Map<Long, List<MaintainPlanContentVO>> map;
		if (1 == param.getGroupType()) {
			nameMap = tasks.stream().collect(Collectors.toMap(MaintainPlanContentVO::getBuildingId, MaintainPlanContentVO::getBuildingName, (t1, t2) -> t1));
			map = tasks.stream().collect(Collectors.groupingBy(MaintainPlanContentVO::getBuildingId));
		} else if (2 == param.getGroupType()) {
			tasks.forEach(e -> e.setElevatorAddress(e.getBuildingName() + "-" + e.getElevatorAddress()));
			nameMap = tasks.stream().collect(Collectors.toMap(MaintainPlanContentVO::getElevatorId, MaintainPlanContentVO::getElevatorAddress, (t1, t2) -> t1));
			map = tasks.stream().collect(Collectors.groupingBy(MaintainPlanContentVO::getElevatorId));
		} else {
			nameMap = tasks.stream().collect(Collectors.toMap(t -> Long.valueOf(t.getBuildingType()), MaintainPlanContentVO::getBuildingTypeName, (t1, t2) -> t1));
			map = tasks.stream().collect(Collectors.groupingBy(t -> Long.valueOf(t.getBuildingType())));
		}
		for (Map.Entry<Long, List<MaintainPlanContentVO>> entry : map.entrySet()) {
			MalfunctionScreenVO malfunctionScreenVO = this.handleMalfunctionScreenDataBetweenDay(entry.getValue(), elevatorEffectedList,DateUtil.betweenDay(param.getBeginDate(), DateUtil.offsetDay(param.getEndDate(),1), Boolean.TRUE));
			malfunctionScreenVO.setProjectId(entry.getKey());
			malfunctionScreenVO.setProjectName(nameMap.get(entry.getKey()));
			resultList.add(malfunctionScreenVO);
		}

		// 取top10
		if (resultList.size() > 10) {
			resultList = resultList.stream().limit(10).collect(Collectors.toList());
		}
		if (resultList.size() > 0) {
			if (1 == param.getSortType()) {
				resultList = resultList.stream().sorted(Comparator.comparing(MalfunctionScreenVO::getApplyRepairTotal).reversed()).collect(Collectors.toList());
			}
			if (2 == param.getSortType()) {
				resultList = resultList.stream().sorted(Comparator.comparing(MalfunctionScreenVO::getEleFaultRatioMonth).reversed()).collect(Collectors.toList());
			}
			if (3 == param.getSortType()) {
				resultList = resultList.stream().sorted(Comparator.comparing(MalfunctionScreenVO::getEleRepeatFaultRatioMonth).reversed()).collect(Collectors.toList());
			}
		}

		return resultList;
	}

	@Override
	public Map<String, MalfunctionScreenVO> repairTrend(AnalysisStatisticalDTO param) {
		Map<String, MalfunctionScreenVO> initDataMap = this.initTrendChartData(param.getBeginDate(), param.getEndDate(), param.getDimension());
		List<Long> allDeptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,allDeptIds));
		List<MaintainPlanContentVO> list = new ArrayList<>();
		List<ElevatorEffectedVO> elevatorEffectedList = new ArrayList<>();
		if(Func.isNotEmpty(elevatorList)){
			List<Long> elevatorIds = elevatorList.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
			list = baseMapper.selectMalfunctionListByDate(param.getBeginDate(), param.getEndDate());
			list = list.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
			elevatorEffectedList = baseMapper.selectElevatorEffectedListByDate(param.getBeginDate(), param.getEndDate());
			elevatorEffectedList = elevatorEffectedList.stream().filter(t -> elevatorIds.contains(t.getElevatorId())).collect(Collectors.toList());
		}
//		if (Func.isNotEmpty(list)) {
//			list = list.stream().filter(e -> e.getWorkOrderType().equals(WorkOrderTypeEnum.CLIENT.getId())).collect(Collectors.toList());
//		}
		// 有效梯
		Map<String, List<MaintainPlanContentVO>> mapData = new HashMap<>(16);
		Map<String, List<ElevatorEffectedVO>> elevatorMap = new HashMap<>(16);
		int dimension = param.getDimension();
		if (!Func.isEmpty(list)) {
			if (dimension == 0) {
				// 日
				mapData = list.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getCreateTime(), "MM/dd")));
				elevatorMap = elevatorEffectedList.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getRecordDate(), "MM/dd")));
			} else if (dimension == 1) {
				// 周
				mapData = list.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "MM/dd"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "MM/dd"))));
				elevatorMap = elevatorEffectedList.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getRecordDate()), "MM/dd"), DateUtil.format(DateUtil.endOfWeek(t.getRecordDate()), "MM/dd"))));
			} else {
				// 月
				mapData = list.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getCreateTime(), "M月")));
				elevatorMap = elevatorEffectedList.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getRecordDate(), "M月")));
			}


		}

		for (Map.Entry<String, MalfunctionScreenVO> dataEntry : initDataMap.entrySet()) {
			if (mapData.containsKey(dataEntry.getKey())) {
				MalfunctionScreenVO malfunctionScreenVO;
				if (dimension == 1) {
					malfunctionScreenVO = this.handleMalfunctionScreenDataMonth(mapData.get(dataEntry.getKey()), elevatorMap.get(dataEntry.getKey()));
				} else {
					malfunctionScreenVO = this.handleMalfunctionScreenData(mapData.get(dataEntry.getKey()), elevatorMap.get(dataEntry.getKey()));
				}
				dataEntry.setValue(malfunctionScreenVO);
			}
		}

		return initDataMap;
	}

	/**
	 * 初始化趋势图数据
	 *
	 * @param beginDate
	 * @param endDate
	 * @param dimension
	 * @return
	 */
	public Map<String, MalfunctionScreenVO> initTrendChartData(Date beginDate, Date endDate, int dimension) {
		Map<String, MalfunctionScreenVO> trendDataMap = new LinkedHashMap<>(32);
		if (dimension == 0) {
			// 日
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				trendDataMap.put(DateUtil.format(date, "MM/dd"), new MalfunctionScreenVO());
			});
		} else if (dimension == 1) {
			// 周
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				String weekDate = String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(date), "MM/dd"), DateUtil.format(DateUtil.endOfWeek(date), "MM/dd"));
				if (!trendDataMap.containsKey(weekDate)) {
					trendDataMap.put(weekDate, new MalfunctionScreenVO());
				}
			});
		} else {
			// 月
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.MONTH);
			rangeDate.forEach(date -> {
				String monthDate = DateUtil.format(date, "M月");
				trendDataMap.put(monthDate, new MalfunctionScreenVO());
			});
		}
		return trendDataMap;
	}

	/**
	 * 封装数据大屏维修数据
	 *
	 * @param tasks                任务list
	 * @param elevatorEffectedList 有效梯list
	 * @return
	 */
	private MalfunctionScreenVO handleMalfunctionScreenData(List<MaintainPlanContentVO> tasks, List<ElevatorEffectedVO> elevatorEffectedList) {
		MalfunctionScreenVO malfunctionScreenVO = new MalfunctionScreenVO();
		long applyRepairTotal = tasks.stream().filter(t -> TaskStatusEnum.WX_CLOSED.getStatus() != t.getStatus() || TaskStatusEnum.WX_TURN_DOWN.getStatus() != t.getStatus()).count();
		malfunctionScreenVO.setApplyRepairTotal(applyRepairTotal);

		// 新增维修任务 (维修任务状态为【待开始】/【进行中】/【已完成】的维修任务工单总数)
		List<Integer> repairTaskStatus = Arrays.asList(TaskStatusEnum.WX_TO_START.getStatus(), TaskStatusEnum.WX_UNDER_WAY.getStatus(), TaskStatusEnum.WX_SIGN_FINISH.getStatus());
		long repairTaskTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).count();
		malfunctionScreenVO.setRepairTaskTotal(repairTaskTotal);

		// 有效梯
		long elevatorTotal = Func.isEmpty(elevatorEffectedList) ? 0 : elevatorEffectedList.stream().map(t -> t.getElevatorId()).distinct().count();
		// 第一次维修任务数
		long firstPlanTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).map(t -> t.getElevatorId()).distinct().count();
		// 重复故障任务数
		long repeatPlanTotal = repairTaskTotal - firstPlanTotal;
		if (elevatorTotal > 0) {
			// 梯故障率 = 故障电梯总数(新增维修任务)/生效电梯数
			BigDecimal eleFaultRatio = new BigDecimal(repairTaskTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleFaultRatio(eleFaultRatio);
			// 重复故障率 (重复故障率 = 重复故障任务总数/生效电梯数)
			BigDecimal eleRepeatFaultRatio = new BigDecimal(repeatPlanTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleRepeatFaultRatio(eleRepeatFaultRatio);
		}

		return malfunctionScreenVO;
	}

	private MalfunctionScreenVO handleMalfunctionScreenDataBetweenDay(List<MaintainPlanContentVO> tasks, List<ElevatorEffectedVO> elevatorEffectedList, Long betweenDay) {
		MalfunctionScreenVO malfunctionScreenVO = new MalfunctionScreenVO();
		long applyRepairTotal = tasks.stream().filter(t -> TaskStatusEnum.WX_CLOSED.getStatus() != t.getStatus() || TaskStatusEnum.WX_TURN_DOWN.getStatus() != t.getStatus()).count();
		malfunctionScreenVO.setApplyRepairTotal(applyRepairTotal);

		// 新增维修任务 (维修任务状态为【待开始】/【进行中】/【已完成】的维修任务工单总数)
		List<Integer> repairTaskStatus = Arrays.asList(TaskStatusEnum.WX_TO_START.getStatus(), TaskStatusEnum.WX_UNDER_WAY.getStatus(), TaskStatusEnum.WX_SIGN_FINISH.getStatus());
		long repairTaskTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).count();
		malfunctionScreenVO.setRepairTaskTotal(repairTaskTotal);

		// 有效梯
		long elevatorTotal = Func.isEmpty(elevatorEffectedList) ? 0 : elevatorEffectedList.stream().map(t -> t.getElevatorId()).distinct().count();
		// 第一次维修任务数
		long firstPlanTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).map(t -> t.getElevatorId()).distinct().count();
		// 重复故障任务数
		long repeatPlanTotal = repairTaskTotal - firstPlanTotal;
		if (elevatorTotal > 0) {
			// 梯故障率 = 故障电梯总数(新增维修任务)/生效电梯数
			BigDecimal eleFaultRatio = new BigDecimal(repairTaskTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleFaultRatio(eleFaultRatio);
			malfunctionScreenVO.setEleFaultRatioMonth(eleFaultRatio.multiply(BigDecimal.valueOf(30)).divide(BigDecimal.valueOf(betweenDay), 3, RoundingMode.HALF_UP));
			// 重复故障率 (重复故障率 = 重复故障任务总数/生效电梯数)
			BigDecimal eleRepeatFaultRatio = new BigDecimal(repeatPlanTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleRepeatFaultRatio(eleRepeatFaultRatio);
			malfunctionScreenVO.setEleRepeatFaultRatioMonth(eleRepeatFaultRatio.multiply(BigDecimal.valueOf(30)).divide(BigDecimal.valueOf(betweenDay), 3, RoundingMode.HALF_UP));
		}

		return malfunctionScreenVO;
	}

	/**
	 * 封装数据大屏维修数据 月化部分
	 *
	 * @param tasks                任务list
	 * @param elevatorEffectedList 有效梯list
	 * @return
	 */
	private MalfunctionScreenVO handleMalfunctionScreenDataMonth(List<MaintainPlanContentVO> tasks, List<ElevatorEffectedVO> elevatorEffectedList) {
		MalfunctionScreenVO malfunctionScreenVO = new MalfunctionScreenVO();
		long applyRepairTotal = tasks.stream().filter(t -> TaskStatusEnum.WX_CLOSED.getStatus() != t.getStatus() || TaskStatusEnum.WX_TURN_DOWN.getStatus() != t.getStatus()).count();
		malfunctionScreenVO.setApplyRepairTotal(applyRepairTotal);

		// 新增维修任务 (维修任务状态为【待开始】/【进行中】/【已完成】的维修任务工单总数)
		List<Integer> repairTaskStatus = Arrays.asList(TaskStatusEnum.WX_TO_START.getStatus(), TaskStatusEnum.WX_UNDER_WAY.getStatus(), TaskStatusEnum.WX_SIGN_FINISH.getStatus());
		long repairTaskTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).count();
		malfunctionScreenVO.setRepairTaskTotal(repairTaskTotal);

		// 有效梯
		long elevatorTotal = Func.isEmpty(elevatorEffectedList) ? 0 : elevatorEffectedList.stream().map(t -> t.getElevatorId()).distinct().count();
		// 第一次维修任务数
		long firstPlanTotal = tasks.stream().filter(t -> repairTaskStatus.contains(t.getStatus())).map(t -> t.getElevatorId()).distinct().count();
		// 重复故障任务数
		long repeatPlanTotal = repairTaskTotal - firstPlanTotal;
		if (elevatorTotal > 0) {
			// 梯故障率 = 故障电梯总数(新增维修任务)/生效电梯数
			BigDecimal eleFaultRatio = new BigDecimal(repairTaskTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleFaultRatio(eleFaultRatio.multiply(BigDecimal.valueOf(30)).divide(BigDecimal.valueOf(7), 3, RoundingMode.HALF_UP));
			// 重复故障率 (重复故障率 = 重复故障任务总数/生效电梯数)
			BigDecimal eleRepeatFaultRatio = new BigDecimal(repeatPlanTotal).divide(new BigDecimal(elevatorTotal), 3, BigDecimal.ROUND_HALF_UP);
			malfunctionScreenVO.setEleRepeatFaultRatio(eleRepeatFaultRatio.multiply(BigDecimal.valueOf(30)).divide(BigDecimal.valueOf(7), 3, RoundingMode.HALF_UP));
		}

		return malfunctionScreenVO;
	}

	@Override
	public ElevatorDetailScreenVO getElevatorDetailScreen() {
		ElevatorDetailScreenVO elevatorDetailScreenVO = baseMapper.getElevatorDetailScreen();
		elevatorDetailScreenVO.setScrapELeTotal(0L);
		elevatorDetailScreenVO.setCancelEleTotal(0L);
		elevatorDetailScreenVO.setStopEleTotal(0L);
		return elevatorDetailScreenVO;
	}

	@Override
	public ByPlanDataScreenVO byPlanDataScreen() {
		ByPlanDataScreenVO dataScreenVO = new ByPlanDataScreenVO();
		List<MaintainPlanContent> list = baseMapper.selectList(Wrappers.<MaintainPlanContent>lambdaQuery().eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId()));
		// 累计保养电梯数
		dataScreenVO.setMaintainEleTotal(list.stream().map(t -> t.getElevatorId()).distinct().count());
		// 今天保养电梯数
		dataScreenVO.setTodayEleTotal(list.stream().filter(t -> DateUtil.isSameDay(t.getPlanDate(), DateUtil.date())).map(t -> t.getElevatorId()).distinct().count());

		Date tomorrow = DateUtil.beginOfDay(DateUtil.tomorrow());
		// 未来3天保养电梯数
		Date threeDayAfter = DateUtil.endOfDay(DateUtil.offsetDay(DateUtil.date(), 3));
		long threeDayAfterEleTotal = list.stream().filter(t -> DateUtil.compare(t.getPlanDate(), tomorrow) >= 0 && DateUtil.compare(t.getPlanDate(), threeDayAfter) <= 0).map(t -> t.getElevatorId()).distinct().count();
		dataScreenVO.setThreeDayAfterEleTotal(threeDayAfterEleTotal);

		// 未来30天保养电梯数
		Date thirtyDayAfter = DateUtil.endOfDay(DateUtil.offsetDay(DateUtil.date(), 30));
		long thirtyDayAfterEleTotal = list.stream().filter(t -> DateUtil.compare(t.getPlanDate(), tomorrow) >= 0 && DateUtil.compare(t.getPlanDate(), thirtyDayAfter) <= 0).map(t -> t.getElevatorId()).distinct().count();
		dataScreenVO.setThirtyDayAfterEleTotal(thirtyDayAfterEleTotal);

		return dataScreenVO;
	}

	@Override
	public List<MaintainCalendarScreenVO> maintainCalendarScreen(MaintainCalendarScreenDTO calendarScreenDTO) {
		List<MaintainCalendarScreenVO> calendarScreenList = new ArrayList<>();
		List<MaintainCalendarDataVO> planList = baseMapper.maintainPlanCalendarList(calendarScreenDTO.getCalendarDate());
		if (Func.isEmpty(planList)) {
			return calendarScreenList;
		}

		Map<String, List<MaintainCalendarDataVO>> planMap = planList.stream().collect(Collectors.groupingBy(t -> DateUtil.formatDate(t.getPlanDate())));
		for (Map.Entry<String, List<MaintainCalendarDataVO>> calendarPlan : planMap.entrySet()) {
			MaintainCalendarScreenVO itemCalendar = new MaintainCalendarScreenVO();
			itemCalendar.setDate(calendarPlan.getKey());
			List<MaintainCalendarDataVO> itemList = calendarPlan.getValue();
			itemCalendar.setPlanTotal(itemList.stream().count());
			// 保养已完成状态
			List<Integer> finishStatus = Arrays.asList(TaskStatusEnum.BY_SIGN.getStatus(), TaskStatusEnum.BY_MAINTAIN_SIGN.getStatus());
			itemCalendar.setFinishTotal(itemList.stream().filter(t -> finishStatus.contains(t.getStatus())).count());

			// 筛选标签
			// 待开始
			if (Func.isNotEmpty(calendarScreenDTO.getStartingTemplates())) {
				List<MaintainCalendarDataVO> startingList = itemList.stream().filter(t -> t.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus())
					&& t.getTemplateSceneList().stream().anyMatch(p -> calendarScreenDTO.getStartingTemplates().contains(p.getSceneTypeId()))).collect(Collectors.toList());
				if (Func.isNotEmpty(startingList)) {
					// 模板场景分类id
					List<MaintainCalendarTemplateSceneVO> startTemplateSceneList = startingList.stream().map(t ->
						t.getTemplateSceneList().stream().filter(p -> calendarScreenDTO.getStartingTemplates().contains(p.getSceneTypeId())).collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MaintainCalendarTemplateSceneVO::getSceneTypeId))), ArrayList::new)))
						.flatMap(List::stream).collect(Collectors.toList());
					itemCalendar.setStartingTags(startTemplateSceneList.stream().collect(Collectors.groupingBy(t -> String.format("%s-%s", t.getSceneTypeId(), t.getSceneName()), Collectors.counting())));
				}
			}
			// 未完成
			if (Func.isNotEmpty(calendarScreenDTO.getNotFinishTemplates())) {
				// 保养未完成状态
				List<Integer> notFinishStatus = Arrays.asList(TaskStatusEnum.BY_PROCESSED.getStatus(), TaskStatusEnum.BY_FINISH.getStatus());
				List<MaintainCalendarDataVO> notFinishList = itemList.stream().filter(t -> notFinishStatus.contains(t.getStatus())
					&& t.getTemplateSceneList().stream().anyMatch(p -> calendarScreenDTO.getNotFinishTemplates().contains(p.getSceneTypeId()))).collect(Collectors.toList());
				if (Func.isNotEmpty(notFinishList)) {
					// 模板场景分类id
					List<MaintainCalendarTemplateSceneVO> notFinishTemplateSceneList = notFinishList.stream().map(t ->
						t.getTemplateSceneList().stream().filter(p -> calendarScreenDTO.getStartingTemplates().contains(p.getSceneTypeId())).collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MaintainCalendarTemplateSceneVO::getSceneTypeId))), ArrayList::new)))
						.flatMap(List::stream).collect(Collectors.toList());
					itemCalendar.setNotFinishTags(notFinishTemplateSceneList.stream().collect(Collectors.groupingBy(t -> String.format("%s-%s", t.getSceneTypeId(), t.getSceneName()), Collectors.counting())));
				}
			}
			// 已完成
			if (Func.isNotEmpty(calendarScreenDTO.getFinishTemplates())) {
				List<MaintainCalendarDataVO> finishList = itemList.stream().filter(t -> finishStatus.contains(t.getStatus())
					&& t.getTemplateSceneList().stream().anyMatch(p -> calendarScreenDTO.getFinishTemplates().contains(p.getSceneTypeId()))).collect(Collectors.toList());
				if (Func.isNotEmpty(finishList)) {
					// 模板场景分类id
					List<MaintainCalendarTemplateSceneVO> finishTemplateSceneList = finishList.stream().map(t ->
						t.getTemplateSceneList().stream().filter(p -> calendarScreenDTO.getStartingTemplates().contains(p.getSceneTypeId())).collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MaintainCalendarTemplateSceneVO::getSceneTypeId))), ArrayList::new)))
						.flatMap(List::stream).collect(Collectors.toList());
					itemCalendar.setFinishTags(finishTemplateSceneList.stream().collect(Collectors.groupingBy(t -> String.format("%s-%s", t.getSceneTypeId(), t.getSceneName()), Collectors.counting())));
				}
			}
			calendarScreenList.add(itemCalendar);
		}

		return calendarScreenList.stream().sorted(Comparator.comparing(MaintainCalendarScreenVO::getDate)).collect(Collectors.toList());
	}

	@Override
	public List<MaintainPlanContent> getList() {

		return baseMapper.getList();
	}

	@Override
	public boolean checkPlanDeferRecord(Long planId) {
		int deferCount = maintainPlanDeferHistoryService.count(Wrappers.<MaintainPlanDeferHistory>lambdaQuery().in(MaintainPlanDeferHistory::getPlanId, planId)
			.notIn(MaintainPlanDeferHistory::getDefertType, 0));
		return deferCount != 0;
	}

	@Override
	public Map<String, Object> selectPlanSimpleDetail(Long planId) {
		Map<String, Object> planSimpleDetail = new HashMap<>();
		MaintainPlanContent planContent = super.getById(planId);
		if (planContent == null) {
			throw new ServiceException("工单id有误,请联系管理员planId=" + planId);
		}
		Map<String, Object> lastPlanSimpleDetail = baseMapper.selectPlanSimpleDetailByElevator(planContent.getElevatorId(), planContent.getPlanDate());
		planSimpleDetail.put("planId", planContent.getId());
		planSimpleDetail.put("planDate", planContent.getPlanDate());
		if (ObjectUtil.isNotEmpty(lastPlanSimpleDetail)) {
			planSimpleDetail.put("lastPlanId", lastPlanSimpleDetail.get("planId"));
			planSimpleDetail.put("lastPlanDate", lastPlanSimpleDetail.get("signatureDate"));
		}
		return planSimpleDetail;
	}

	@Override
	public List<User> getUserListByElevatorIds(List<Long> elevatorIds) {
		return baseMapper.getUserListByElevatorIds(elevatorIds);
	}

	@Override
	public List<Map<String, Object>> estateSugarCheckPlanTop(String tenantId) {
		return baseMapper.estateSugarCheckPlanTop(tenantId);
	}

	@Override
	public List<MaintainPlanContent> estateSugarCheckPlanFinishStatus(String startDate, String endDate, List<Long> elevatorIds) {
		return baseMapper.estateSugarCheckPlanFinishStatus(startDate, endDate, elevatorIds);
	}

	@Override
	public PlanOtherInfoVo getPlanOtherInfo(Long planId) {
		final String tenantId = AuthUtil.getTenantId();
		PlanOtherInfoVo planOtherInfoVo = baseMapper.getPlanOtherInfo(planId, tenantId);

		Integer days = Convert.toInt(DateUtil.betweenDay(planOtherInfoVo.getPlanDate(), planOtherInfoVo.getPlanEndTime() == null ? new Date() : planOtherInfoVo.getPlanEndTime(), true));
		if (days == 0) {
			planOtherInfoVo.setTime("false");
			planOtherInfoVo.setStatus(1);
		} else {
			planOtherInfoVo.setTime("3天");
			planOtherInfoVo.setStatus(2);
		}
		return planOtherInfoVo;
	}

	@Override
	public FutureMaintainPlanStatisticalVO futureMaintainPlanStatistical(Long userId) {
		List<FutureMaintainPlanVO> futurePlanList = baseMapper.selectFutureMaintainPlanByUserId(userId);
		long moreUserPlanCount = futurePlanList.stream().filter(t -> t.getPlanUserList().size() > 1).count();

		// 封装数据
		FutureMaintainPlanStatisticalVO planStatisticalVO = new FutureMaintainPlanStatisticalVO();
		planStatisticalVO.setTotal(futurePlanList.stream().count());
		planStatisticalVO.setMoreUserPlanCount(moreUserPlanCount);
		planStatisticalVO.setOnlyUserPlanCount(futurePlanList.stream().count() - moreUserPlanCount);

		return planStatisticalVO;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean moveUserFuturePlan(MoveUserPlanDTO moveUserPlanDTO) {
		List<FutureMaintainPlanVO> futurePlanList = baseMapper.selectFutureMaintainPlanByUserId(moveUserPlanDTO.getLeaveUserId());
		if (moveUserPlanDTO.getType() == 0) {
			futurePlanList = futurePlanList.stream().filter(t -> t.getPlanUserList().size() > 1).collect(Collectors.toList());
		} else {
			futurePlanList = futurePlanList.stream().filter(t -> t.getPlanUserList().size() == 1).collect(Collectors.toList());
		}

		if (Func.isEmpty(futurePlanList)) {
			throw new ServiceException("没有需要转移保养计划的数据!");
		}
		int checkLeave = userService.count(Wrappers.<User>lambdaQuery().eq(User::getStatus, 2).in(User::getId, moveUserPlanDTO.getNewUserIds()));
		if (checkLeave > 0) {
			throw new ServiceException("不能转移给已离职人员!");
		}

		// 删除的数据
		List<Long> delPlanContentUserIdList = new ArrayList<>();
		// 增加的数据
		List<MaintainPlanContentUser> planContentUserList = new ArrayList<>();
		for (FutureMaintainPlanVO futurePlan : futurePlanList) {
			List<SimplePlanUserVO> planUserList = futurePlan.getPlanUserList();
			List<Long> planContentUserIds = planUserList.stream().map(t -> t.getPlanContentUserId()).collect(Collectors.toList());
			delPlanContentUserIdList.addAll(planContentUserIds);

			Set<Long> planUsers = planUserList.stream().map(t -> t.getUserId()).collect(Collectors.toSet());
			// 合并 旧userId + 新userId
			planUsers.addAll(moveUserPlanDTO.getNewUserIds());
			// 移除离职人员userId
			planUsers.removeAll(Arrays.asList(moveUserPlanDTO.getLeaveUserId()));
			for (Long planUserId : planUsers) {
				MaintainPlanContentUser maintainPlanContentUser = new MaintainPlanContentUser();
				maintainPlanContentUser.setPlanId(futurePlan.getPlanId());
				maintainPlanContentUser.setUserId(planUserId);
				planContentUserList.add(maintainPlanContentUser);
			}
		}

		return planContentUserService.deleteLogic(delPlanContentUserIdList) && planContentUserService.saveBatch(planContentUserList);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean delUserFuturePlan(Long leaveUserId) {
		List<FutureMaintainPlanVO> futurePlanList = baseMapper.selectFutureMaintainPlanByUserId(leaveUserId)
			.stream().filter(t -> t.getPlanUserList().size() > 1).collect(Collectors.toList());
		if (Func.isEmpty(futurePlanList)) {
			throw new ServiceException("没有需要解除保养计划的数据!");
		}

		List<Long> delPlanContentUserIds = futurePlanList.stream().map(t -> {
			Long planUserId = t.getPlanUserList().stream()
				.filter(p -> leaveUserId.equals(p.getUserId()))
				.map(p -> p.getPlanContentUserId())
				.findFirst().get();
			return planUserId;
		}).collect(Collectors.toList());

		return planContentUserService.deleteLogic(delPlanContentUserIds);
	}

	@Override
	public PlanApproveVO partApproveStatistical(Long userId) {
		PlanApproveVO planApproveVO = new PlanApproveVO();
		List<PlanApproveUserVO> partApproveList = baseMapper.partApproveStatistical(userId);
		if (Func.isEmpty(partApproveList)) {
			planApproveVO.setTotal(0L);
			planApproveVO.setApproveCount(0L);
			return planApproveVO;
		}

		planApproveVO.setTotal(partApproveList.stream().count());
		planApproveVO.setApproveCount(partApproveList.stream().count());
		planApproveVO.setPlanIds(partApproveList.stream().map(t -> t.getPlanId()).collect(Collectors.toList()));

		return planApproveVO;
	}

	@MsgNotice(businessType = BusinessType.RESCUE, node = TriggerNodeEnum.CREATE, parameterSource = 1, parameterName = "result")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Long addRescuePlan(MaintainMalfunctionRescue maintainMalfunctionRescue) {
		Building building = buildingService.getById(maintainMalfunctionRescue.getBuildingId());
		if (Func.isEmpty(building)) {
			throw new ServiceException("系统中不存在该项目!");
		}
		Long elevatorId = ObjectUtil.defaultIfNull(maintainMalfunctionRescue.getElevatorId(), 0L);
		if (elevatorId > 0) {
			Elevator elevator = elevatorService.getById(elevatorId);
			if (Func.isEmpty(elevator)) {
				throw new ServiceException("系统中不存在该电梯!");
			}
			if (!Func.equalsSafe(elevator.getBuildingId(), maintainMalfunctionRescue.getBuildingId())) {
				throw new ServiceException("项目信息与电梯信息不一致!");
			}
			// 重复创建校验 有在途的工单，则不能创建工单
			List<Integer> statusList = Arrays.asList(TaskStatusEnum.JY_DISPATCH.getStatus(), TaskStatusEnum.JY_TO_START.getStatus(), TaskStatusEnum.JY_PROCESSED.getStatus(), TaskStatusEnum.JY_ARRIVE.getStatus());
			if (this.count(Wrappers.<MaintainPlanContent>lambdaQuery()
				.eq(MaintainPlanContent::getElevatorId, elevatorId)
				.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
				.in(MaintainPlanContent::getStatus, statusList)) > 0) {
				throw new ServiceException("当前电梯已关联救援单，请在救援单完成或撤销后再进行操作!");
			}
		}

		// 租户id
		String tenantId = Func.isBlank(maintainMalfunctionRescue.getTenantId()) ? AuthUtil.getTenantId() : maintainMalfunctionRescue.getTenantId();

		// 插入工单信息
		MaintainPlanContent planContent = new MaintainPlanContent();
		planContent.setElevatorId(elevatorId);
		planContent.setTypeStatus(MaintainPlanContentEnum.MAINTAIN_KR.getId());
		planContent.setPlanDate(new Date());
		planContent.setStatus(TaskStatusEnum.JY_DISPATCH.getStatus());
		planContent.setTenantId(tenantId);
		planContent.setTlwJoinId(maintainMalfunctionRescue.getTlwJoinId());
		this.save(planContent);

		// 任务扩展表
		MaintainPlanContentExt contentExt = new MaintainPlanContentExt();
		contentExt.setPlanId(planContent.getId());
		contentExt.setPlanBeginTime(LocalDateTime.now());
		contentExt.setTenantId(tenantId);
		maintainPlanContentExtService.save(contentExt);

		// 插入救援明细
		MaintainMalfunctionRescue rescue = new MaintainMalfunctionRescue();
		BeanUtils.copyProperties(maintainMalfunctionRescue, rescue);
		rescue.setPlanId(planContent.getId());
		rescue.setElevatorId(elevatorId);
		// 查询项目要求救援到达时间
		BuildingRescueTimeVO rescueTimeVO = buildingExtService.getBuildingRescueTime(maintainMalfunctionRescue.getBuildingId(), planContent.getCreateTime());
		rescue.setMaximumType(rescueTimeVO.getType());
		rescue.setMaximumArrivalTime(DateUtil.offsetMinute(planContent.getCreateTime(), rescueTimeVO.getRescueUseTime()));
		maintainMalfunctionRescueService.save(rescue);

		// 插入任务跟踪信息
		if (maintainMalfunctionRescue.getFrom() == 1) {
			malfunctionLogService.insertWorkLog(planContent.getId(), TaskStatusEnum.JY_DISPATCH.getName(), planContent.getStatus(), null, null, "一键呼救");
		} else {
			malfunctionLogService.insertWorkLog(planContent.getId(), TaskStatusEnum.JY_DISPATCH.getName(), planContent.getStatus());
		}

		/**
		 * 发消息通知规则
		 * 优先电梯-管理人，无管理人则项目联系人
		 */
//		StringBuilder address = new StringBuilder(maintainMalfunctionRescue.getAddress());
//		if (Func.isNotBlank(maintainMalfunctionRescue.getFloor())) {
//			address.append("-楼层");
//			address.append(maintainMalfunctionRescue.getFloor());
//			address.append("层");
//		}
//		List<SimpleUserVO> simpleUsers = new ArrayList<>();
//		if (elevatorId > 0) {
//			List<Long> userIds = estateElevatorUserService.getEstateElevatorSafeUser(elevatorId);
//			if (Func.isNotEmpty(userIds)) {
//				simpleUsers = userService.getEstateSimpleUserInfo(userIds);
//			}
//		}
//		if (Func.isEmpty(simpleUsers)) {
//			BuildingContacts buildingContacts = buildingContactsService.getOne(Wrappers.<BuildingContacts>lambdaQuery().eq(BuildingContacts::getBuildingId, maintainMalfunctionRescue.getBuildingId()), false);
//			if (Func.isNotEmpty(buildingContacts) && Func.isNotBlank(buildingContacts.getPhone())) {
//				SimpleUserVO simpleUser = userService.getEstateSimpleUserInfoByPhone(buildingContacts.getPhone());
//				if (Func.isNotEmpty(simpleUser)) {
//					simpleUsers.add(simpleUser);
//				}
//			}
//		}
//
//		//  发送消息填回来
//		if (Func.isNotEmpty(simpleUsers)) {
//			for (SimpleUserVO userInfo : simpleUsers) {
//				// 发送模板消息
//				if (elevatorId > 0 && Func.isNotBlank(userInfo.getOpenid())) {
//					// 当前系统环境
//					String curEnvironment = environment.getActiveProfiles()[0];
////					HashMap<String, Object> map = new HashMap<>(4);
//					HashMap<String, String> templateMap = new HashMap<>(8);
//					// 跳转链接
//					String skipUrl = String.format("/pages/elevatorTask/rescue/detail?planId=%s&elevatorId=%s", planContent.getId(), elevatorId);
//					templateMap.put("first", "应急救援进度通知");
//					templateMap.put("keyword1", building.getName());
//					templateMap.put("keyword2", address.toString());
//					templateMap.put("keyword3", "救援中");
//					templateMap.put("keyword4", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm"));
//					templateMap.put("remark", building.getAddress());
////					if (Func.equalsSafe(curEnvironment, "prod")) {
////						templateMap.put("params", skipUrl);
////						try {
////							templateMap.put("params", URLEncoder.encode(skipUrl, "UTF-8"));
////						} catch (UnsupportedEncodingException e) {
////							templateMap.put("params", skipUrl);
////						}
////					}
////					map.put("openid", userInfo.getOpenid());
////					map.put("params", templateMap);
////					map.put("templateId", Func.equalsSafe(curEnvironment, "prod") ? TemplateMsgConstant.T_RESCUE_RATE_ID_PROD : TemplateMsgConstant.T_RESCUE_RATE_ID_TEST);
////					map.put("templateId", TemplateMsgConstant.T_RESCUE_RATE_ID_PROD);
//					log.info("==========创建救援工单-发送应急救援进度模板消息,userId: {}, openid: {}", userInfo.getUserId(), userInfo.getOpenid());
////					asyncUtils.createPost(projectApiConfiguration.getEstate() + official_accounts_push_url, JSONObject.toJSONString(map));
//					Map<String, String> variablesMap = new HashMap<>(4);
//					variablesMap.put("content", JSONObject.toJSONString(templateMap));
//					variablesMap.put("url", skipUrl);
//					variablesMap.put(BladeConstant.MDC_TENANT_ID_KEY, planContent.getTenantId());
//					// 维保小程序appid
//					variablesMap.put("miniProgramId", "wxb8dc0f5451620604");
//
//					SendRequest sendRequest = SendRequest.builder()
//						.messageTemplateId("1920670346995929089")
//						.messageParam(MessageParam.builder()
//							.receiver(String.valueOf(userInfo.getOpenid()))
//							.variables(variablesMap)
//							.build())
//						.build();
//					R r = messageCenterUtils.sendMessage(sendRequest);
//					log.info("公众号推送结果:" + com.alibaba.fastjson.JSON.toJSONString(r));
//				}
//
//				//发送短信
//				HashMap<String, String> smsMap = new HashMap<>(2);
//				smsMap.put("place", building.getName() + "-" + address);
//				log.info("==========创建救援工单-发送应急救援进度短信消息,userId: {}, phone: {}", userInfo.getUserId(), userInfo.getPhone());
////				asyncUtils.sendSms(MaintenanceSmsConstant.ESTATE_RESCUE, smsMap, userInfo.getPhone());
//				// 构造消息
//				SendRequest sendRequest = SendRequest.builder()
//					.messageTemplateId("1920674215402508289")
//					.messageParam(MessageParam.builder()
//						.receiver(userInfo.getPhone())
//						.variables(smsMap)
//						.build())
//					.build();
//				R r = messageCenterUtils.sendMessage(sendRequest);
//				log.info("公众号推送结果:" + com.alibaba.fastjson.JSON.toJSONString(r));
//
//				//发送语音
//				Map<String, String> phoneMap = new HashMap<>(2);
//				phoneMap.put("place", building.getName().replaceAll("-", "") + address.toString().replace("-", ""));
////				VoiceTemplate voiceTemplate = new VoiceTemplate();
////				voiceTemplate.setTemplateCode(VoiceConstant.ESTATE_RESCUE);
////				voiceTemplate.setTemplateParam(phoneMap);
////				voiceTemplate.setPhoneNumber(userInfo.getPhone());
//				log.info("==========创建救援工单-拨打应急救援进度电话,userId: {}, phone: {}", userInfo.getUserId(), userInfo.getPhone());
////				asyncUtils.sendPhone(voiceTemplate);
//				// 构造消息
//				sendRequest = SendRequest.builder()
//					.messageTemplateId("1920685153799213058")
//					.messageParam(MessageParam.builder()
//						.receiver(userInfo.getPhone())
//						.variables(phoneMap)
//						.build())
//					.build();
//				r = messageCenterUtils.sendMessage(sendRequest);
//				log.info("公众号推送结果:" + com.alibaba.fastjson.JSON.toJSONString(r));
//
//			}
//		}

		SimpleElevatorInfoVO elevatorInfo = elevatorDeviceService.getSimpleElevatorInfoByElevatorId(elevatorId);
		if (Func.isNotEmpty(elevatorInfo)) {
			RescueOrderDetail rescueOrderDetail = new RescueOrderDetail();
			rescueOrderDetail.setPlanId(planContent.getId());
			rescueOrderDetail.setDeviceId(elevatorInfo.getDeviceId());
			rescueOrderDetail.setElevatorId(elevatorInfo.getElevatorId());
			rescueOrderDetail.setStatus(TaskStatusEnum.JY_DISPATCH.getStatus());
			rescueOrderDetail.setStatusDate(planContent.getUpdateTime());
			rescueOrderDetail.setProjectName(building.getName());
			rescueOrderDetail.setLongitude(building.getLongitude());
			rescueOrderDetail.setLatitude(building.getLatitude());
			Tenant tenant = tenantService.getByTenantId(tenantId);
			rescueOrderDetail.setCompany(tenant == null ? null : tenant.getTenantName());
			rescueOrderDetail.setElevatorPosition(maintainMalfunctionRescue.getAddress());
			rescueOrderDetail.setCurrentFloor(maintainMalfunctionRescue.getFloor());
			ElevatorDetail elevatorDetail = elevatorDetailService.getOne(Wrappers.<ElevatorDetail>lambdaQuery().eq(ElevatorDetail::getElevatorId, elevatorId), false);
			rescueOrderDetail.setElevatorCode(elevatorDetail == null ? null : elevatorDetail.getElevatorNum());
			rescueOrderDetail.setRescueId(maintainMalfunctionRescue.getRescueId());
			// 推送实时信息到梯联网
			eventPublisher.publishEvent(new TlwRescueInfoEvent(rescueOrderDetail));
		}

		if (elevatorId != 0) {
			MaintainPlanConfig planConfig = maintainPlanConfigService.getOne(Wrappers.<MaintainPlanConfig>lambdaQuery().eq(MaintainPlanConfig::getElevatorId, elevatorId).eq(MaintainPlanConfig::getIsDeleted, 0));
			if (ObjectUtil.isNull(planConfig)) {
				throw new ServiceException("救援工单创建失败，未配置电梯保养计划");
			}
		}

		planContent = super.getById(planContent.getId());
		// rescueTimeOut
		TenantConfig tenantConfig = tenantConfigService.getTenantConfig(planContent.getTenantId());
		// 要求救援时间阔值
		int rescueTimeOut = tenantConfig.getConfig().containsKey("rescueTimeOut") ? (int) Double.parseDouble(String.valueOf(tenantConfig.getConfig().get("rescueTimeOut"))) : 5;
		long sendTime = planContent.getCreateTime().getTime() + (Long.valueOf(rescueTimeVO.getRescueUseTime()) - rescueTimeOut) * 60 * 1000;
		long sendArriveTime = planContent.getCreateTime().getTime() + Long.valueOf(rescueTimeVO.getRescueUseTime()) * 60 * 1000;
		JSONObject send = new JSONObject();
		send.put("planId", planContent.getId());
		send.put("bType", RescueArriveEnum.UPCOMING.getId());
		send.put("rescueTimeOut", rescueTimeOut);
		send.put("timeOut", DateUtil.formatDateTime(rescue.getMaximumArrivalTime()));
		mqMessageService.sendRescueArrive(sendTime, send.toJSONString());
		JSONObject sendArrive = new JSONObject();
		sendArrive.put("planId", planContent.getId());
		sendArrive.put("bType", RescueArriveEnum.OVERTIME.getId());
		sendArrive.put("timeOut", DateUtil.formatDateTime(rescue.getMaximumArrivalTime()));
		mqMessageService.sendRescueArrive(sendArriveTime, sendArrive.toJSONString());

		// 未及时调度时间阔值
		int overtimeNotDispatch = tenantConfig.getConfig().containsKey("overtimeNotDispatch") ? (int) Double.parseDouble(String.valueOf(tenantConfig.getConfig().get("overtimeNotDispatch"))) : 2;
		long sendNotDispatchTime = planContent.getCreateTime().getTime() + Long.valueOf(overtimeNotDispatch) * 60 * 1000;
		JSONObject sendNotDispatch = new JSONObject();
		sendNotDispatch.put("planId", planContent.getId());
		sendNotDispatch.put("bType", RescueArriveEnum.OVERTIME_NOT_DISPATCH.getId());
		sendNotDispatch.put("overtimeNotDispatch", overtimeNotDispatch);
		mqMessageService.sendRescueArrive(sendNotDispatchTime, sendNotDispatch.toJSONString());


		// 更新电梯停梯状态
		Elevator elevator = elevatorService.getById(planContent.getElevatorId());
		if (ObjectUtil.isNotEmpty(elevator) && ObjectUtil.isNotEmpty(elevator.getIsStop())) {
			if (!elevator.getIsStop().equals(ElevatorStatusEnum.STOP.getStatus())) {
				ElevatorStopLog elevatorStopLog = new ElevatorStopLog();
				elevatorStopLog.setElevatorId(planContent.getElevatorId());
				elevatorStopLog.setInitStatus(elevator.getIsStop());
				elevatorStopLog.setSetStatus(ElevatorStatusEnum.STOP.getStatus());
				elevatorStopLog.setModule(String.valueOf(planContent.getId()));
				elevatorStopLog.setPosition(5);
				elevatorStopLogService.insertStopLog(elevatorStopLog);

				elevator.setIsStop(ElevatorStatusEnum.STOP.getStatus());
				elevatorService.updateById(elevator);
			}
		}
		// 2024-08-14 新增发送微信消息卡片，仅在电梯已知情况下发送
		if (elevatorId > 0) {
			RobotContentDTO robotContentDTO = new RobotContentDTO();
			robotContentDTO.setMaintainType(MaintainPlanContentEnum.MAINTAIN_KR.getId());
			robotContentDTO.setMaintainSubTypeDesc("【救援调度中】");
			ElevatorRescueRobotSimpleVO elevatorRescueInfo = this.getElevatorSimpleInfoByPlanId(planContent.getId());
			robotContentDTO.setLocationDesc(elevatorRescueInfo.getBuildingName() + "-" + elevatorRescueInfo.getElevatorAddress());
			asyncUtils.createPost(projectApiConfiguration.getMaintain() + "/repair/robot/sendRobotMessage/" + planContent.getId() + "/" + elevatorId, JSONObject.toJSONString(robotContentDTO), WebUtil.getRequest());
		}
		return planContent.getId();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Long addTlwRescuePlan(TlwMalfunctionRescueDTO tlwMalfunctionRescue) {
		SimpleElevatorInfoVO simpleElevatorInfoVO = elevatorDeviceService.getSimpleElevatorInfo(tlwMalfunctionRescue.getDeviceId());
		if (Func.isEmpty(simpleElevatorInfoVO)) {
			throw new ServiceException("救援工单创建失败，系统未找到该电梯");
		}

//		int rescuingCounts = baseMapper.selectRescuingByElevatorIdCounts(simpleElevatorInfoVO.getElevatorId());
//		if (rescuingCounts > 0) {
//			throw new ServiceException("救援工单创建失败，电梯已有正在进行救援工单");
//		}

		MaintainMalfunctionRescue maintainMalfunctionRescue = new MaintainMalfunctionRescue();
		maintainMalfunctionRescue.setTlwJoinId(tlwMalfunctionRescue.getTlwJoinId());
		maintainMalfunctionRescue.setBuildingId(simpleElevatorInfoVO.getBuildingId());
		maintainMalfunctionRescue.setElevatorId(simpleElevatorInfoVO.getElevatorId());
		maintainMalfunctionRescue.setAddress(simpleElevatorInfoVO.getElevatorAddress());
		maintainMalfunctionRescue.setFloor(tlwMalfunctionRescue.getFloor());
		maintainMalfunctionRescue.setName(tlwMalfunctionRescue.getName());
		maintainMalfunctionRescue.setPhone(tlwMalfunctionRescue.getPhone());
		maintainMalfunctionRescue.setTrapDesc(tlwMalfunctionRescue.getTrapDesc());
		maintainMalfunctionRescue.setRescueId(tlwMalfunctionRescue.getRescueId());
		if (tlwMalfunctionRescue.getFrom() == 1) {
			maintainMalfunctionRescue.setTrapFrom("1_6");
			maintainMalfunctionRescue.setTrapFromValue("一键呼救");
		} else {
			maintainMalfunctionRescue.setTrapFrom("1_3");
			maintainMalfunctionRescue.setTrapFromValue("梯联网报警");
		}
		maintainMalfunctionRescue.setTenantId(simpleElevatorInfoVO.getTenantId());
		maintainMalfunctionRescue.setFrom(tlwMalfunctionRescue.getFrom());
		return this.addRescuePlan(maintainMalfunctionRescue);
	}

	@Override
	public List<RescueDetailVO> rescueDetailListByElevatorId(Long buildingId, Long elevatorId) {
		List<RescueDetailVO> list = baseMapper.rescueDetailListByElevatorId(buildingId, elevatorId);
		return MaintainPlanContentWrapper.build().convertListByStatus(list);
	}

	@Override
	public UserDimissionCountVO selectUserDimissionCount(Long userId) {

		UserDimissionCountVO countVO = new UserDimissionCountVO();

		// 获取人员责任梯
		List<ElevatorPrincipalUser> principalUsers = elevatorPrincipalUserService.list(Wrappers.<ElevatorPrincipalUser>lambdaQuery().eq(ElevatorPrincipalUser::getUserId, userId));
		// 统计维保责任梯
		if (ObjectUtil.isNotEmpty(principalUsers)) {
			countVO.setPrincipalTotal(principalUsers.stream().count());
			countVO.setMaiPrincipal(principalUsers.stream().filter(e -> e.getType().equals(ElevatorPrincipalUserEnum.MAI.getType())).count());
		}
		// 获取人员关联工单
		List<MaintainPlanContent> planContents = planContentUserService.selectPlanContentByUser(userId);
		if (ObjectUtil.isNotEmpty(planContents)) {

			// 保养工单
			List<MaintainPlanContent> chePlan = planContents.stream().filter(e -> e.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_BY.getId())).collect(Collectors.toList());
			if (ObjectUtil.isNotEmpty(chePlan)) {
				// 保养工单总数
				countVO.setCheTotal(chePlan.stream().count());
				// 保养待开始
				countVO.setCheToStart(chePlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.BY_DISABLE.getStatus())).count());
				// 保养进行中
				countVO.setCheUnderway(chePlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.BY_PROCESSED.getStatus())).count());
				// 保养已提交
				countVO.setCheCommit(chePlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.BY_FINISH.getStatus())).count());
				// 保养维保签字
				countVO.setCheMaiSignature(chePlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.BY_MAINTAIN_SIGN.getStatus())).count());
				countVO.setChePlanIdList(chePlan.stream().map(MaintainPlanContent::getId).collect(Collectors.toList()));
			}

			// 维修工单
			List<MaintainPlanContent> malPlan = planContents.stream().filter(e -> e.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_WX.getId())).collect(Collectors.toList());
			if (ObjectUtil.isNotEmpty(malPlan)) {
				// 维修工单总数
				countVO.setMalTotal(malPlan.stream().count());
				// 维修待开始
				countVO.setMalToStart(malPlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.WX_TO_START.getStatus())).count());
				// 维修进行中
				countVO.setMalUnderway(malPlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.WX_UNDER_WAY.getStatus())).count());
				countVO.setMalPlanIdList(malPlan.stream().map(MaintainPlanContent::getId).collect(Collectors.toList()));
			}

			// 部件工单
			List<MaintainPlanContent> partPlan = planContents.stream().filter(e -> e.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_PARTS.getId())).collect(Collectors.toList());
			if (ObjectUtil.isNotEmpty(partPlan)) {
				// 部件工单总数
				countVO.setPartTotal(partPlan.stream().count());
				// 部件待开始
				countVO.setPartToStart(partPlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.PARTS_TO_START.getStatus())).count());
				// 部件进行中
				countVO.setPartUnderway(partPlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.PARTS_CHANGE.getStatus())).count());
				countVO.setPartPlanIdList(partPlan.stream().map(MaintainPlanContent::getId).collect(Collectors.toList()));
			}

			// 救援工单
			List<MaintainPlanContent> rescuePlan = planContents.stream().filter(e -> e.getTypeStatus().equals(MaintainPlanContentEnum.MAINTAIN_KR.getId())).collect(Collectors.toList());
			if (ObjectUtil.isNotEmpty(rescuePlan)) {
				// 救援工单总数
				countVO.setRescueTotal(rescuePlan.stream().count());
				// 救援已出发
				countVO.setRescueProcessed(rescuePlan.stream().filter(e -> e.getStatus().equals(TaskStatusEnum.JY_PROCESSED.getStatus())).count());
				countVO.setRescuePlanIdList(rescuePlan.stream().map(MaintainPlanContent::getId).collect(Collectors.toList()));
			}
		}
		return countVO;
	}

	@Override
	public Boolean deleteDimissionUserPlan(Long userId) {
		List<MaintainPlanContentUser> users = baseMapper.selectDimissionUserPlan(userId);
		if (ObjectUtil.isNotEmpty(users)) {
			return planContentUserService.removeByIds(users.stream().map(MaintainPlanContentUser::getId).collect(Collectors.toList()));
		}
		return Boolean.TRUE;
	}

	@Override
	public List<Map<String, Object>> selectLeaveUserPlanStatus(MaintainLeaveUserPlanQuery planQuery) {
		List<MaintainPlanContent> planContents = this.listByIds(planQuery.getPlanIds());
		List<Integer> statusList = planContents.stream().map(MaintainPlanContent::getStatus).distinct().collect(Collectors.toList());
		List<Map<String, Object>> resultList = new ArrayList<>();
		statusList.forEach(e -> {
			Map<String, Object> statusMap = new HashMap<>();
			statusMap.put("value", e);
			statusMap.put("label", TaskStatusEnum.getTaskName(planQuery.getTypeStatus(), e));
			resultList.add(statusMap);
		});
		List<Integer> taskStatusVOS = TaskStatusEnum.getTaskStatusByType(planQuery.getTypeStatus()).stream().map(TaskStatusVO::getStatus).collect(Collectors.toList());
		resultList.sort((o1, o2) -> {
				if (taskStatusVOS.contains((Integer) o1.get("value")) && taskStatusVOS.contains((Integer) o2.get("value"))) {
					return Integer.compare(taskStatusVOS.indexOf((Integer) o1.get("value")), taskStatusVOS.indexOf((Integer) o2.get("value")));
				} else if (taskStatusVOS.contains((Integer) o1.get("value")) && !taskStatusVOS.contains((Integer) o2.get("value"))) {
					return -1;
				} else if (!taskStatusVOS.contains((Integer) o1.get("value")) && taskStatusVOS.contains((Integer) o2.get("value"))) {
					return 1;
				}
				return 0;
			}
		);
		return resultList;
	}

	@Override
	public Boolean disposeSignatureTime() {
		List<MaintainPlanContentExt> planContentExts = maintainPlanContentExtService.listAllPlanDisposeSignatureTime();
		if (ObjectUtil.isNotEmpty(planContentExts)) {
			planContentExts.forEach(e -> {
				if (ObjectUtil.isEmpty(e.getMaintainSignatureTime())) {
					e.setMaintainSignatureTime(e.getPlanEndTime());
				}
			});
			return maintainPlanContentExtService.updateBatchById(planContentExts);
		}
		return null;
	}

	@Override
	public List<PlanInitVo> getPlanListInitData() {
		return baseMapper.getPlanListInitData();
	}

	@Override
	public List<TlwByPlanVO> tlwByPlanPage(TlwPlanQueryDTO queryDTO, IPage<TlwByPlanVO> page) {
		return baseMapper.tlwByPlanPage(queryDTO, page);
	}

	@Override
	public List<TlwByPlanVO> tlwByPlanList(TlwPlanQueryDTO queryDTO) {
		return baseMapper.tlwByPlanList(queryDTO);
	}

	@Override
	public TlwByPlanVO tlwByPlanDetail(Long planId) {
		return baseMapper.tlwByPlanDetail(planId);
	}

	@Override
	public List<MaintainPlanContent> screenUserPlanStatisticsList(List<Long> userIds) {
		return baseMapper.screenUserPlanStatisticsList(userIds);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updateMessage(PlanContentUpdateDto planContentUpdateDto) {
		final String tenantId = Func.isBlank(planContentUpdateDto.getTenantId()) ? AuthUtil.getTenantId() : planContentUpdateDto.getTenantId();
		// 1. 必传信息完整性校验
		Long elevatorId = planContentUpdateDto.getElevatorId();
		Long planId = planContentUpdateDto.getPlanId();
		if (Func.isNull(planId)) {
			throw new ServiceException("工单ID不可为空");
		}
		// 2. 根据所关联的表修改信息
		// 主表
		MaintainPlanContent currentPlan = this.getOne(new LambdaQueryWrapper<MaintainPlanContent>()
			.eq(BaseEntity::getId, planId)
			.eq(BaseEntity::getIsDeleted, 0)
		);
		if (currentPlan.getStatus() > TaskStatusEnum.WX_TO_START.getStatus()) {
			throw new ServiceException("工单状态已经变更，无法更新，请刷新当前页面");
		}
		// boring set methods
		currentPlan.setElevatorId(elevatorId);
		currentPlan.setTypeStatus(planContentUpdateDto.getTypeStatus());
		if (!Func.isNull(planContentUpdateDto.getStatus())) {
			currentPlan.setStatus(planContentUpdateDto.getStatus());
		}
		currentPlan.setTlwJoinId(planContentUpdateDto.getTlwJoinId());
		currentPlan.setTenantId(tenantId);
		// 2024-04-24 新增字段
		if (!Func.isNull(planContentUpdateDto.getSourcePlanType())) {
			List<Long> fromPlanIds = getFromPlanIdByOwnId(planId);
			if (Func.isNotEmpty(fromPlanIds)) {
				Integer sourcePlanType = currentPlan.getSourcePlanType();
				if (!planContentUpdateDto.getSourcePlanType().equals(sourcePlanType)) {
					throw new ServiceException("该工单已有关联工单来源，无法修改");
				}
			} else {
				currentPlan.setSourcePlanType(planContentUpdateDto.getSourcePlanType());
			}
		} else {
			currentPlan.setSourcePlanType(null);
		}
		// 使用updateWrapper更新主表，解决null无法更新的问题
		LambdaUpdateWrapper<MaintainPlanContent> updateWrapper = new LambdaUpdateWrapper<MaintainPlanContent>();
		updateWrapper.eq(MaintainPlanContent::getId, planId);
		updateWrapper.set(MaintainPlanContent::getElevatorId, elevatorId)
			.set(MaintainPlanContent::getTypeStatus, currentPlan.getTypeStatus())
			.set(MaintainPlanContent::getProcessDefinitionId, currentPlan.getProcessDefinitionId())
			.set(MaintainPlanContent::getProcessInstanceId, currentPlan.getProcessInstanceId())
			.set(MaintainPlanContent::getPlanDate, currentPlan.getPlanDate())
			.set(MaintainPlanContent::getFinishDate, currentPlan.getFinishDate())
			.set(MaintainPlanContent::getIsDeferred, currentPlan.getIsDeferred())
			.set(MaintainPlanContent::getPlanFrom, currentPlan.getPlanFrom())
			.set(MaintainPlanContent::getTlwJoinId, currentPlan.getTlwJoinId())
			.set(MaintainPlanContent::getSourcePlanType, currentPlan.getSourcePlanType())
			.set(MaintainPlanContent::getPlanFinishDate, currentPlan.getPlanFinishDate());
		boolean main = this.update(updateWrapper);

		// 维修详情
		if (Func.isEmpty(planContentUpdateDto.getWorks())) {
			throw new ServiceException("维修报障信息不可为空");
		}
		planContentUpdateDto.getWorks().forEach(e -> {
			e.setPlanId(planContentUpdateDto.getPlanId());
		});
		malfunctionWorkService.remove(Wrappers.<MaintainMalfunctionWork>lambdaQuery().eq(MaintainMalfunctionWork::getPlanId, planContentUpdateDto.getPlanId()));
		boolean detail = malfunctionWorkService.saveBatch(planContentUpdateDto.getWorks());

		// 拓展表
		MaintainPlanContentExt maintainPlanContentExt = maintainPlanContentExtService.getOne(new LambdaQueryWrapper<MaintainPlanContentExt>()
			.eq(MaintainPlanContentExt::getPlanId, planId)
			.eq(BaseEntity::getIsDeleted, 0)
		);
		maintainPlanContentExt.setElevatorStop(planContentUpdateDto.getIsStop());
		maintainPlanContentExt.setEmerProcType(planContentUpdateDto.getEmerProcType());
		maintainPlanContentExt.setWorkOrderType(planContentUpdateDto.getWorkOrderType());
		maintainPlanContentExt.setFaultCompany(tenantService.getOne(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenantId)).getTenantName());
		maintainPlanContentExt.setTenantId(tenantId);
		boolean ext = maintainPlanContentExtService.updateById(maintainPlanContentExt);

		// 推送通知
		List<Integer> triggerConditions = new ArrayList<>();
		// 停梯1小时预警通知
//		triggerConditions.add(TriggerConditionEnum.CONDITION_6.getCode());
		// 停梯2小时预警通知
		triggerConditions.add(TriggerConditionEnum.CONDITION_7.getCode());
		// 停梯4小时预警通知
//		triggerConditions.add(TriggerConditionEnum.CONDITION_8.getCode());
		Map<String, Object> callback = new HashMap<>();
		callback.put("elevatorId", elevatorId);
		callback.put("msgCode", TriggerConditionEnum.CONDITION_7.getCode());
		for (Integer triggerCondition : triggerConditions) {
			eventPublisher.publishEvent(new NoticeEvent(NoticeTaskParam.builder()
				.businessType(BusinessType.REPAIR.getCode())
				.triggerNode(TriggerNodeEnum.CREATE.getCode())
				.triggerCondition(triggerCondition)
				.bizId(planId)
				.tenantId(AuthUtil.getTenantId())
				.callback(callback)
				.build()
			));
		}

		Elevator elevator = elevatorService.getById(currentPlan.getElevatorId());
		if (ObjectUtil.isNotEmpty(elevator) && ObjectUtil.isNotEmpty(elevator.getIsStop())) {
			if (!elevator.getIsStop().equals(planContentUpdateDto.getIsStop())) {
				ElevatorStopLog elevatorStopLog = new ElevatorStopLog();
				elevatorStopLog.setElevatorId(currentPlan.getElevatorId());
				elevatorStopLog.setInitStatus(elevator.getIsStop());
				elevatorStopLog.setSetStatus(planContentUpdateDto.getIsStop());
				elevatorStopLog.setModule(String.valueOf(currentPlan.getId()));
				elevatorStopLog.setPosition(3);
				elevatorStopLogService.insertStopLog(elevatorStopLog);

				elevator.setIsStop(planContentUpdateDto.getIsStop());
				elevatorService.updateById(elevator);
			}
		}

		return main && detail && ext;
	}

	@Override
	public MaintainPlanContent selectById(Long planId) {
		return baseMapper.getMaintainPlanById(planId);
	}

	@Override
	public List<ElevatorTaskStatusVO> getElevatorTaskStatusList(List<String> deviceIds) {
		return baseMapper.getElevatorTaskStatusList(deviceIds);
	}

	@Override
	public List<MaintainPlanContentVO> getTlwElevatorStatistics(String deviceId, Date startDate, Date endDate) {
		return baseMapper.getTlwElevatorStatistics(deviceId, startDate, endDate);
	}

	@Override
	public Integer getRepairElevatorCount(Date beginTimeDate, Date endTimeDate, List<Long> elevatorIds) {
		return baseMapper.getRepairElevatorCount(beginTimeDate, endTimeDate,elevatorIds);
	}

	@Override
	public Integer getVeryMuchProblemElevatorCount(Date beginTimeDate, Date endTimeDate, List<Long> elevatorIds) {
		return baseMapper.getVeryMuchProblemElevatorCount(beginTimeDate, endTimeDate,elevatorIds);
	}

	@Override
	public Map<String, Long> elevatorTrend(AnalysisStatisticalDTO param) {
		// 初始化数据
		Map<String, Long> initDataMap = new LinkedHashMap<>(32);
		int dimension = param.getDimension();
		Date beginDate = param.getBeginDate();
		Date endDate = param.getEndDate();
		if (dimension == 0) {
			// 日
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				initDataMap.put(DateUtil.format(date, "MM/dd"), 0L);
			});
		} else if (dimension == 1) {
			// 周
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				String weekDate = String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(date), "MM/dd"), DateUtil.format(DateUtil.endOfWeek(date), "MM/dd"));
				if (!initDataMap.containsKey(weekDate)) {
					initDataMap.put(weekDate, 0L);
				}
			});
		} else {
			// 月
			List<DateTime> rangeDate = DateUtil.rangeToList(beginDate, endDate, DateField.MONTH);
			rangeDate.forEach(date -> {
				String monthDate = DateUtil.format(date, "M月");
				initDataMap.put(monthDate, 0L);
			});
		}

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		Map<String, Long> data = new HashMap<>(32);
		List<MaintainPlanContentVO> list = new ArrayList<>();
		if(Func.isNotEmpty(elevatorIds)){
			list = baseMapper.selectMalfunctionListByDate(param.getBeginDate(), param.getEndDate()).stream().sorted(Comparator.comparing(t -> t.getCreateTime())).collect(Collectors.toList());
			list = list.stream().filter(e -> elevatorIds.contains(e.getElevatorId())).collect(Collectors.toList());
		}
		if (!Func.isEmpty(list)) {
			list = list.stream().filter(e -> e.getWorkOrderType().equals(WorkOrderTypeEnum.CLIENT.getId())).collect(Collectors.toList());
			if (dimension == 0) {
				// 日
				data = list.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getCreateTime(), "MM/dd"), Collectors.mapping(t -> t.getElevatorId(),
					Collectors.collectingAndThen(Collectors.toSet(), t -> t.stream().count()))));
			} else if (dimension == 1) {
				// 周
				data = list.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "MM/dd"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "MM/dd")),
					Collectors.mapping(t -> t.getElevatorId(), Collectors.collectingAndThen(Collectors.toSet(), t -> t.stream().count()))));
			} else {
				// 月
				data = list.stream().collect(Collectors.groupingBy(t -> DateUtil.format(t.getCreateTime(), "M月"), Collectors.mapping(t -> t.getElevatorId(),
					Collectors.collectingAndThen(Collectors.toSet(), t -> t.stream().count()))));
			}


		}

		for (Map.Entry<String, Long> dataEntry : initDataMap.entrySet()) {
			if (data.containsKey(dataEntry.getKey())) {
				initDataMap.put(dataEntry.getKey(), data.get(dataEntry.getKey()));
			}
		}

		return initDataMap;
	}

	@Override
	public List<NewRepairScreenSourceVO> getPlanSources(Date beginTimeDate, Date endTimeDate) {
		List<NewRepairScreenSourceVO> result = new ArrayList<>(6);
		NewRepairScreenTempVO tempData = baseMapper.getPlanSourceMap(beginTimeDate, endTimeDate);
		// 1. 获取值,计算各占比
		String total = "总数";
		String estateCount = "物业端报障";
		String wxCount = "微信群报障";
		String phoneCount = "服务电话报障";
		String systemCount = "系统派单";
		String repairCount = "维保端报障";
		String visitorCount = "游客报障";
		// 总值
		Integer totalCount = tempData.getTotal();
		if (!totalCount.equals(0)) {
			// 赋值操作
			NewRepairScreenSourceVO estateResult = new NewRepairScreenSourceVO();
			estateResult.setSource(estateCount);
			estateResult.setCounts(tempData.getEstateCount());
			estateResult.setPercentage(formatPercentage(tempData.getEstateCount(), totalCount));
			NewRepairScreenSourceVO wxResult = new NewRepairScreenSourceVO();
			wxResult.setSource(wxCount);
			wxResult.setCounts(tempData.getWxCount());
			wxResult.setPercentage(formatPercentage(tempData.getWxCount(), totalCount));
			NewRepairScreenSourceVO phoneResult = new NewRepairScreenSourceVO();
			phoneResult.setSource(phoneCount);
			phoneResult.setCounts(tempData.getPhoneCount());
			phoneResult.setPercentage(formatPercentage(tempData.getPhoneCount(), totalCount));
			NewRepairScreenSourceVO systemResult = new NewRepairScreenSourceVO();
			systemResult.setSource(systemCount);
			systemResult.setCounts(tempData.getSystemCount());
			systemResult.setPercentage(formatPercentage(tempData.getSystemCount(), totalCount));
			NewRepairScreenSourceVO repairResult = new NewRepairScreenSourceVO();
			repairResult.setSource(repairCount);
			repairResult.setCounts(tempData.getRepairCount());
			repairResult.setPercentage(formatPercentage(tempData.getRepairCount(), totalCount));
			NewRepairScreenSourceVO visitorResult = new NewRepairScreenSourceVO();
			visitorResult.setSource(visitorCount);
			visitorResult.setCounts(tempData.getVisitorCount());
			visitorResult.setPercentage(formatPercentage(tempData.getVisitorCount(), totalCount));

			result.add(estateResult);
			result.add(wxResult);
			result.add(phoneResult);
			result.add(systemResult);
			result.add(repairResult);
			result.add(visitorResult);
			// 根据数量倒排序，且0都不返回
			result = result.stream()
				.filter(f -> !f.getCounts().equals(0))
				.sorted(Comparator.comparing(NewRepairScreenSourceVO::getCounts).reversed())
				.collect(Collectors.toList());

		}
		return result;
	}

	private String formatPercentage(Integer value, Integer size) {

		return new BigDecimal(value)
			.divide(new BigDecimal(size), 5, RoundingMode.HALF_UP)
			.multiply(new BigDecimal(100))
			.setScale(1, RoundingMode.HALF_UP)
			+ "%";

	}

	@Override
	public MalPlanOverviewVO overview(AnalysisStatisticalDTO param) {

		List<Long> deptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<MaintainPlanContentExtVO> list = baseMapper.selectMalfunctionExtListByDate(param.getBeginDate(), param.getEndDate());
		MalPlanOverviewVO malPlanOverviewVO = new MalPlanOverviewVO();
		if(Func.isEmpty(elevatorIds)){
			return malPlanOverviewVO;
		}
		if (Func.isEmpty(list)) {
			return malPlanOverviewVO;
		}
		list = list.stream().filter(e -> elevatorIds.contains(e.getElevatorId()) && e.getWorkOrderType().equals(WorkOrderTypeEnum.CLIENT.getId())).collect(Collectors.toList());
		if (Func.isEmpty(list)) {
			return malPlanOverviewVO;
		}

		// 工单总数
		long total = list.stream().mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setTotal(total);
		// 停梯总数
		long stopTotal = list.stream().filter(t -> t.getElevatorStop() != null && t.getElevatorStop() == 2).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setStopTotal(stopTotal);

		// 转派工单数
		long assignmentCount = list.stream().filter(t -> t.getAssignmentCounts() > 1).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setAssignmentCount(assignmentCount);

		// 工单最大转派工单数
		int maxAssignment = list.stream().mapToInt(t -> t.getAssignmentCounts()).max().getAsInt();
		malPlanOverviewVO.setMaxAssignment(maxAssignment);

		// 待指派
		long allocationCount = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_ALLOCATION.getStatus()).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setAllocationCount(allocationCount);

		// 待开始
		long toStartCount = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_TO_START.getStatus()).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setToStartCount(toStartCount);

		// 维修中
		long underWayCount = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_UNDER_WAY.getStatus()).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setUnderWayCount(underWayCount);

		// 已完成
		long finishCount = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus()).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setFinishCount(finishCount);

		// 关门工单
		long closeCount = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_CHECK_PASS_CLOSED.getStatus()).mapToLong(MaintainPlanContentExtVO::getPlanId).distinct().count();
		malPlanOverviewVO.setCloseCount(closeCount);

		return malPlanOverviewVO;
	}

	@Override
	public OrderDetailStatisticsVO orderDetailStatistics(AnalysisStatisticalDTO param) {

		OrderDetailStatisticsVO statistics = new OrderDetailStatisticsVO();
		List<Long> deptIds = deptService.selectDeptAndAllChildIds(param.getDeptIds());
		List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId,deptIds));
		List<Long> elevatorIds = elevatorDepts.stream().map(ElevatorPrincipalDept::getElevatorId).distinct().collect(Collectors.toList());
		List<MaintainPlanContentExtVO> list = baseMapper.selectMalfunctionExtListByDate(param.getBeginDate(), param.getEndDate());
		if (Func.isEmpty(elevatorIds)) {
			return statistics;
		}
		if (Func.isEmpty(list)) {
			return statistics;
		}
		list = list.stream().filter(e -> elevatorIds.contains(e.getElevatorId()) && e.getWorkOrderType().equals(WorkOrderTypeEnum.CLIENT.getId())).collect(Collectors.toList());
		if (Func.isEmpty(list)) {
			return statistics;
		}

		List<MaintainPlanContentExtVO> assignmentList = list.stream().filter(t -> t.getAllocateDuration() != null).collect(Collectors.toList());
		if (Func.isNotEmpty(assignmentList)) {
			// 平均调度时长
			OptionalDouble avgAssignmentTime = assignmentList.stream().mapToLong(t -> t.getAllocateDuration()).average();
			if (avgAssignmentTime.isPresent()) {
				statistics.setAvgAssignmentTime(DateUtils.millisecondToTime(new BigDecimal(avgAssignmentTime.getAsDouble()).longValue()));
			}

			// 调度耗时最大值
			long maxAssignmentTime = assignmentList.stream().mapToLong(t -> t.getAllocateDuration()).max().getAsLong();
			statistics.setMaxAssignmentTime(DateUtils.millisecondToTime(maxAssignmentTime));

			// 调度耗时最小值
			long minAssignmentTime = assignmentList.stream().mapToLong(t -> t.getAllocateDuration()).min().getAsLong();
			statistics.setMinAssignmentTime(DateUtils.millisecondToTime(minAssignmentTime));
		}

		List<MaintainPlanContentExtVO> arriveList = list.stream().filter(t -> t.getArriveTime() != null).collect(Collectors.toList());
		if (Func.isNotEmpty(arriveList)) {
			// 平均到达时长
			OptionalDouble avgArriveTime = arriveList.stream().mapToLong(t -> t.getArriveDuration()).average();
			if (avgArriveTime.isPresent()) {
				statistics.setAvgArriveTime(DateUtils.millisecondToTime(new BigDecimal(avgArriveTime.getAsDouble()).longValue()));
			}

			// 到达耗时列表
			long[] arriveTimeArr = arriveList.stream().mapToLong(t -> t.getArriveDuration()).sorted().toArray();
			int arriveTimeLength = arriveTimeArr.length;
			// 到达耗时中位数
			long medianArriveTime;
			if (arriveTimeLength % 2 == 0) {
				// 偶数
				medianArriveTime = (arriveTimeArr[arriveTimeLength / 2 - 1] + arriveTimeArr[arriveTimeLength / 2]) / 2;
			} else {
				medianArriveTime = arriveTimeArr[arriveTimeLength / 2];
			}
			statistics.setMedianArriveTime(DateUtils.millisecondToTime(medianArriveTime));

			// 到达耗时最小值
			long minArriveTime = arriveList.stream().mapToLong(t -> t.getArriveDuration()).min().getAsLong();
			statistics.setMinArriveTime(DateUtils.millisecondToTime(minArriveTime));
		}

		//已完成的工单
		List<MaintainPlanContentExtVO> finishList = list.stream().filter(t -> t.getPlanStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus()).collect(Collectors.toList());
		if (Func.isNotEmpty(finishList)) {
			// 维修平均时间
			OptionalDouble avgMalTime = finishList.stream().mapToLong(t -> t.getWorkMill()).average();
			if (avgMalTime.isPresent()) {
				statistics.setAvgMalTime(DateUtils.millisecondToTime(new BigDecimal(avgMalTime.getAsDouble()).longValue()));
			}

			// 维修耗时中位数
			long[] malTimeArr = finishList.stream().mapToLong(t -> t.getWorkMill()).sorted().toArray();
			int malTimeLength = malTimeArr.length;
			// 维修耗时中位数
			long medianMalTime;
			if (malTimeLength % 2 == 0) {
				// 偶数
				medianMalTime = (malTimeArr[malTimeLength / 2 - 1] + malTimeArr[malTimeLength / 2]) / 2;
			} else {
				medianMalTime = malTimeArr[malTimeLength / 2];
			}
			statistics.setMedianMalTime(DateUtils.millisecondToTime(medianMalTime));

			// 维修耗时最小值
			long minMalTime = finishList.stream().mapToLong(t -> t.getWorkMill()).min().getAsLong();
			statistics.setMinMalTime(DateUtils.millisecondToTime(minMalTime));

			// 工单平均时长
			OptionalDouble avgOrderTime = finishList.stream().mapToLong(t -> t.getOrderTimeMillisecond()).average();
			if (avgOrderTime.isPresent()) {
				statistics.setAvgOrderTime(DateUtils.millisecondToTime(new BigDecimal(avgOrderTime.getAsDouble()).longValue()));
			}

			// 工单耗时中位数
			long[] orderTimeArr = finishList.stream().mapToLong(t -> t.getOrderTimeMillisecond()).sorted().toArray();
			int orderTimeLength = orderTimeArr.length;
			// 维修耗时中位数
			long medianOrderTime;
			if (orderTimeLength % 2 == 0) {
				// 偶数
				medianOrderTime = (orderTimeArr[orderTimeLength / 2 - 1] + orderTimeArr[orderTimeLength / 2]) / 2;
			} else {
				medianOrderTime = orderTimeArr[orderTimeLength / 2];
			}
			statistics.setMedianOrderTime(DateUtils.millisecondToTime(medianOrderTime));

			// 工单耗时最小值
			long minOrderTime = finishList.stream().mapToLong(t -> t.getOrderTimeMillisecond()).min().getAsLong();
			statistics.setMinOrderTime(DateUtils.millisecondToTime(minOrderTime));
		}

		return statistics;
	}

	@Override
	public List<ChartVO> problemItemStatistics(AnalysisStatisticalDTO param) {
		List<ChartVO> data = new ArrayList<>();
		List<MaintainPlanContentExtVO> list = baseMapper.selectMalfunctionExtListByDate(param.getBeginDate(), param.getEndDate());
		List<String> itemList = list.stream().filter(t -> Func.isNotBlank(t.getProblemItem())).map(t -> t.getProblemItem()).flatMap(t -> Arrays.stream(t.split(","))).collect(Collectors.toList());
		Map<String, Long> mapData = itemList.stream().collect(Collectors.groupingBy(t -> t, Collectors.counting()));

		// 字典数据
		List<Dict> dictList = DictCache.getList("fault_problem");
		Map<String, String> dictMap = dictList.stream().collect(Collectors.toMap(Dict::getDictKey, Dict::getDictValue));

		// 数据封装
		mapData.forEach((k, v) -> {
			ChartVO chart = new ChartVO();
			chart.setName(dictMap.get(k));
			chart.setTotal(v);
			chart.setPercentage(NumberUtil.div(String.valueOf(v), String.valueOf(itemList.size()), 3));
			data.add(chart);
		});

		return data.size() > 0 ? data.stream().sorted(Comparator.comparing(ChartVO::getTotal).reversed()).collect(Collectors.toList()) : data;
	}

	@Override
	public List<MaintainPlanContentVO> selectMalfunctionListInMonthPlus(Date beginTimeDate, Date endTimeDate) {
		return baseMapper.selectMalfunctionListInMonthPlus(beginTimeDate, endTimeDate);
	}

	@Override
	public List<ElevatorEffectedVO> selectElevatorEffectedListInMonthPlus(Date beginTimeDate, Date endTimeDate) {
		return baseMapper.selectElevatorEffectedListInMonthPlus(beginTimeDate, endTimeDate);
	}

	/**
	 * 2023-11-13 事务工单批量关闭
	 *
	 * @param newWorkPlanCloseDTO 事务工单id 集合
	 * @return 是否成功
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean closeBatch(NewWorkPlanCloseDTO newWorkPlanCloseDTO) {
		List<Long> planIds = newWorkPlanCloseDTO.getPlanIds();
		List<MaintainPlanContent> targetList = this.list(new LambdaQueryWrapper<MaintainPlanContent>()
			.in(BaseEntity::getId, planIds)
		);
		// 增加工单限制
		targetList.forEach(
			t -> {
				Integer status = t.getStatus();
				if (status.equals(TaskStatusEnum.SW_UNDER_WAY.getStatus())
					|| status.equals(TaskStatusEnum.SW_SIGN_FINISH.getStatus())) {
					throw new ServiceException("业务异常！不允许关闭进行中、已完成的工单！请重新选择");
				}
			}
		);
		targetList.forEach(t -> t.setStatus(TaskStatusEnum.SW_CLOSED.getStatus()));
		// 更新任务追踪状态
		List<MaintainMalfunctionLog> updateList = new ArrayList<>();
		Long executorId = AuthUtil.getUserId();
		String executor = userService.getById(executorId).getRealName();
		for (Long planId : planIds) {
			MaintainMalfunctionLog maintainMalfunctionLog = new MaintainMalfunctionLog();
			maintainMalfunctionLog.setPlanId(planId);
			maintainMalfunctionLog.setPlanStatus(TaskStatusEnum.SW_CLOSED.getStatus());
			maintainMalfunctionLog.setExecutor(executor);
			maintainMalfunctionLog.setTypeName("关闭工单");
			updateList.add(maintainMalfunctionLog);
		}
		malfunctionLogService.saveBatch(updateList);
		return this.updateBatchById(targetList);
	}

	@Override
	public List<MaintainPlanContent> listWxPlan(Date beginTimeDate, Date endTimeDate) {
		return baseMapper.listWxPlan(beginTimeDate, endTimeDate);
	}

	@Override
	public List<ByPlanImageListVO> selectByAllImage(String startDate, String endDate) {
		List<ByPlanImageSimpleListVO> planList = baseMapper.selectByAllImage(startDate, endDate);
		if (Func.isNotEmpty(planList)) {
			List<ByPlanImageListVO> resultList = new ArrayList<>();
			planList.forEach(e -> {
				if (Func.isNotEmpty(e.getSignList())) {
					e.getSignList().forEach(j -> {
						ByPlanImageListVO listVO = new ByPlanImageListVO();
						listVO.setPlanId(e.getPlanId());
						listVO.setUrl(j.getSignImage());
						listVO.setSignId(j.getId());
						listVO.setImageFrom(1);
						resultList.add(listVO);
					});
					e.getSignList().forEach(j -> {
						ByPlanImageListVO listVO = new ByPlanImageListVO();
						listVO.setPlanId(e.getPlanId());
						listVO.setUrl(j.getSignOutImage());
						listVO.setSignId(j.getId());
						listVO.setImageFrom(2);
						resultList.add(listVO);
					});
				}
				if (Func.isNotEmpty(e.getCheckHistoryVOS())) {
					e.getCheckHistoryVOS().forEach(j -> {
						List<String> urlList = WatermarkImgUtils.findImgUrl(j.getRecordData().toString() + j.getRecordVOS().toString());
						if (Func.isNotEmpty(urlList)) {
							urlList.forEach(k -> {
								ByPlanImageListVO listVO = new ByPlanImageListVO();
								listVO.setPlanId(e.getPlanId());
								listVO.setLocationTypeId(j.getLocationType());
								listVO.setLocationTypeName(j.getLocationTypeName());
								listVO.setTemplateContentId(j.getTemplateContentId());
								listVO.setTemplateContentName(j.getTemplateContentName());
								listVO.setUrl(k);
								listVO.setImageFrom(3);
								resultList.add(listVO);
							});
						}
					});
				}
			});
			return resultList;
		}
		return null;
	}

	@Override
	public IPage<PlanListVo> selectPlanOnlyDayList(PlanSearchDTO planSearchDTO) {

		planSearchDTO.setTenantId(AuthUtil.getTenantId());
		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		IPage<PlanListVo> iPage = Condition.getPage(query);

		List<PlanListVo> planListVos;
		if (planSearchDTO.getPlanStatus().contains(TaskStatusEnum.BY_DISABLE.getStatus())) {
			planListVos = baseMapper.selectPlanOnlyDayListDisable(iPage, planSearchDTO);
		} else if (planSearchDTO.getPlanStatus().contains(TaskStatusEnum.BY_PROCESSED.getStatus())
			|| planSearchDTO.getPlanStatus().contains(TaskStatusEnum.BY_FINISH.getStatus())) {
			planListVos = baseMapper.selectPlanOnlyDayListProcessedAndCommit(iPage, planSearchDTO);
		} else {
			planListVos = baseMapper.selectPlanOnlyDayListFinish(iPage, planSearchDTO);
		}
		iPage.setRecords(planListVos);
		return iPage;
	}

	@Override
	public IPage<PlanListVo> planListNew(PlanSearchDTO planSearchDTO) {

		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		IPage<PlanListVo> iPage = Condition.getPage(query);

		if (Func.isEmpty(planSearchDTO.getPlanStatus())) {
			List<Integer> planStatus = TaskStatusEnum.getTaskStatusByType(MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.stream().map(TaskStatusVO::getStatus).collect(Collectors.toList());
			planSearchDTO.setPlanStatus(planStatus);
		}
		if (Func.isNotEmpty(planSearchDTO.getElevatorDeptIds())) {
			List<ElevatorPrincipalDept> elevatorDeptList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId, planSearchDTO.getElevatorDeptIds()).isNotNull(ElevatorPrincipalDept::getElevatorId));
			if (Func.isNotEmpty(elevatorDeptList)) {
				List<Long> elevatorIds = elevatorDeptList.stream().map(ElevatorPrincipalDept::getElevatorId).collect(Collectors.toList());
				if (Func.isNotEmpty(planSearchDTO.getElevatorIds())) {
					planSearchDTO.getElevatorIds().addAll(elevatorIds);
				} else {
					planSearchDTO.setElevatorIds(elevatorIds);
				}
			} else {
				return iPage;
			}
		}

		planSearchDTO.setTenantId(AuthUtil.getTenantId());
		List<PlanListVo> planListVos = baseMapper.selectByPlanList(planSearchDTO.getSize() == 0 ? null : iPage, planSearchDTO);
		Map<Long, List<PlanUserAllDTO>> planUserGroup = new HashMap<>();
		Map<Long, List<ElevatorPrincipalDept>> elevatorDeptGroup = new HashMap<>();
		if (Func.isNotEmpty(planListVos)) {
			List<Long> planIds = planListVos.stream().map(PlanListVo::getPlanId).collect(Collectors.toList());
			List<PlanUserAllDTO> planUserAllDTOS = baseMapper.selectPlanUserAll(planIds);
			planUserGroup = planUserAllDTOS.stream().collect(Collectors.groupingBy(PlanUserAllDTO::getPlanId));

			List<Long> elevatorIds = planListVos.stream().map(PlanListVo::getElevatorId).distinct().collect(Collectors.toList());
			List<ElevatorPrincipalDept> elevatorDepts = elevatorPrincipalDeptService.listElevatorDeptByElevatorIds(elevatorIds);
			if(Func.isNotEmpty(elevatorDepts)){
				elevatorDeptGroup = elevatorDepts.stream().collect(Collectors.groupingBy(ElevatorPrincipalDept::getElevatorId));
			}
		}

		for (PlanListVo planListVo : planListVos) {

			if (planUserGroup.containsKey(planListVo.getPlanId())) {
				List<PlanUserAllDTO> planUserAll = planUserGroup.get(planListVo.getPlanId());
				planListVo.setRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable().equals(1)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
				planListVo.setPersonnelRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable().equals(2)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
			}

			if(elevatorDeptGroup.containsKey(planListVo.getElevatorId())){
				List<String> deptNames;
				if(Func.isNotEmpty(planSearchDTO.getElevatorDeptIds())){
					deptNames = elevatorDeptGroup.get(planListVo.getElevatorId()).stream().filter(t -> planSearchDTO.getElevatorDeptIds().contains(t.getDeptId())).map(ElevatorPrincipalDept::getDeptName).collect(Collectors.toList());
				}else {
					deptNames = elevatorDeptGroup.get(planListVo.getElevatorId()).stream().map(ElevatorPrincipalDept::getDeptName).collect(Collectors.toList());
				}
				if(Func.isNotEmpty(deptNames)){
					planListVo.setElevatorDeptNames(String.join(",",deptNames));
				}
			}

			if (Func.isEmpty(planListVo.getOverdueDay())) {
				planListVo.setOverdueDayName("及时");
			}

		}
		iPage.setRecords(planListVos);
		return iPage;
	}

	@Override
	public IPage<PlanListVo> planListNewDown(PlanSearchDTO planSearchDTO) {

		Query query = new Query();
		query.setSize(planSearchDTO.getSize());
		query.setCurrent(planSearchDTO.getCurrent());
		IPage<PlanListVo> iPage = Condition.getPage(query);

		int SING_STATUS = 2;//表示签退签到异常

		if (Func.isEmpty(planSearchDTO.getPlanStatus())) {
			List<Integer> planStatus = TaskStatusEnum.getTaskStatusByType(MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.stream().map(TaskStatusVO::getStatus).collect(Collectors.toList());
			planSearchDTO.setPlanStatus(planStatus);
		}
		if (Func.isNotEmpty(planSearchDTO.getElevatorDeptIds())) {
			List<ElevatorPrincipalDept> elevatorDeptList = elevatorPrincipalDeptService.list(Wrappers.<ElevatorPrincipalDept>lambdaQuery().in(ElevatorPrincipalDept::getDeptId, planSearchDTO.getElevatorDeptIds()).isNotNull(ElevatorPrincipalDept::getElevatorId));
			if (Func.isNotEmpty(elevatorDeptList)) {
				List<Long> elevatorIds = elevatorDeptList.stream().map(ElevatorPrincipalDept::getElevatorId).collect(Collectors.toList());
				if (Func.isNotEmpty(planSearchDTO.getElevatorIds())) {
					planSearchDTO.getElevatorIds().addAll(elevatorIds);
				} else {
					planSearchDTO.setElevatorIds(elevatorIds);
				}
			} else {
				return iPage;
			}
		}

		planSearchDTO.setTenantId(AuthUtil.getTenantId());
		List<PlanListVo> planListVos = baseMapper.selectByPlanListDown(planSearchDTO.getSize() == 0 ? null : iPage, planSearchDTO);
		Map<Long, List<PlanUserAllDTO>> planUserGroup = new HashMap<>();
		if (Func.isNotEmpty(planListVos)) {
			List<Long> planIds = planListVos.stream().map(PlanListVo::getPlanId).collect(Collectors.toList());
			List<PlanUserAllDTO> planUserAllDTOS = baseMapper.selectPlanUserAll(planIds);
			planUserGroup = planUserAllDTOS.stream().collect(Collectors.groupingBy(PlanUserAllDTO::getPlanId));
		}
		final List<Long> planIds = planListVos.stream().filter(o -> o.getPlanStatus() != TaskStatusEnum.BY_DISABLE.getStatus()).map(PlanListVo::getPlanId).collect(Collectors.toList());
		List<MaintainSign> maintainSignList = new ArrayList<>(16);
		if (planIds.size() > 0) {
			maintainSignList = maintainSignService.getMaintainSignList(planIds);
		}


		//处理逾期未逾期的判断逻辑,
		//表示年审工单
		Integer auditType = 2;
//		//逾期类型1- 非年审梯,1:已逾期,2:未逾期
//		Integer[] overdueTypes = {0, -1, 1, 2};
		for (PlanListVo planListVo : planListVos) {

			if (planUserGroup.containsKey(planListVo.getPlanId())) {
				List<PlanUserAllDTO> planUserAll = planUserGroup.get(planListVo.getPlanId());
				planListVo.setRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable().equals(1)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
				planListVo.setPersonnelRealNames(planUserAll.stream().filter(e -> e.getPlanUserFromTable().equals(2)).map(PlanUserAllDTO::getRealName).collect(Collectors.joining(",")));
			}

			if (Func.isEmpty(planListVo.getOverdueDay())) {
				planListVo.setOverdueDayName("及时");
			}
//			planListVo.setOverdueType(overdueTypes[1]);
//			if (auditType.equals(planListVo.getAuditType())) {
//				//1:工单时间如果大于今天 表示未逾期
//				Integer date1 = Convert.toInt(DateUtil.format(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), "yyyyMMdd"));
//				Integer date2 = Convert.toInt(DateUtil.format(new Date(), "yyyyMMdd"));
//				if (date1 > date2) {
//					planListVo.setOverdueType(overdueTypes[3]);
//					isOverdueTomorrow(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), new Date(), planListVo);
//				} else {
//					Date date = planListVo.getMaintainSignatureTime() != null ? planListVo.getMaintainSignatureTime() : new Date();
//					Integer date3 = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
//					Integer date4 = Convert.toInt(DateUtil.format(planListVo.getOverdueDate(), "yyyyMMdd"));
//					if (date3 > date4) {
//						planListVo.setOverdueType(overdueTypes[2]);
//					} else {
//						//如果维保签字时间和临界时间相等表示未逾期
//						if (date3.equals(date4)) {
//							planListVo.setOverdueType(overdueTypes[3]);
//							isOverdueTomorrow(date, new Date(), planListVo);
//						} else if (date4 < Convert.toInt(DateUtil.format(date, "yyyyMMdd"))) {
//							planListVo.setOverdueType(overdueTypes[2]);
//						} else {
//							//临界时间大于维保签字时间未逾期
//							planListVo.setOverdueType(overdueTypes[3]);
//							//临界值小于明天，然后判断时间差
//							isOverdueTomorrow(planListVo.getOverdueDate(), new Date(), planListVo);
//						}
//					}
//				}
//			}
			if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
				List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
				List<SignStatusInfoVo> resultList = new ArrayList<>(5);
				if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
					List<PlanUserSignDetailVo> userSignInfoList2 = planListVo.getUserSignInfoList2();
					List<String> collect = userSignInfoList2.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
					if (userSignInfoList != null && userSignInfoList.size() > 0) {
						for (SignStatusInfoVo signStatusInfoVo : userSignInfoList) {
							if (collect.contains(signStatusInfoVo.getUserName())) {
								resultList.add(signStatusInfoVo);
							}
							planListVo.setUserSignInfoList(resultList);
						}
					}

				}
			}
			// 处理每个用户的签到信息
			if (planIds.size() > 0) {
				final Map<Long, List<MaintainSign>> map = maintainSignList.stream().collect(Collectors.groupingBy(MaintainSign::getPlanId));
				final List<MaintainSign> maintainSigns = map.get(planListVo.getPlanId());
				if (maintainSigns == null) {
					continue;
				}
				final List<Long> userIds = maintainSigns.stream().map(MaintainSign::getUserId).distinct().collect(Collectors.toList());
				final Map<Long, List<MaintainSign>> map1 = maintainSigns.stream().collect(Collectors.groupingBy(MaintainSign::getUserId));
				int signTypeStatus = 2;//表示正常
				List<PlanSignStatusVo> planSignStatusList = new ArrayList<>(5);
				for (Long userId : userIds) {
					List<MaintainSign> maintainSigns1 = map1.get(userId);
					final List<Integer> signStatus = maintainSigns1.stream().map(MaintainSign::getSignStatus).distinct().collect(Collectors.toList());
					final List<Integer> signOutStatus = maintainSigns1.stream().map(MaintainSign::getSignOutStatus).distinct().collect(Collectors.toList());
					PlanSignStatusVo planSignStatusVo = new PlanSignStatusVo();
					planSignStatusVo.setUserName(maintainSigns1.get(0).getUserName());
					planSignStatusVo.setSignStatusName(signStatus.contains(signTypeStatus) == true ? "签到异常" : "签到正常");
					// 离线签到
					if (signStatus.contains(3)) {
						planSignStatusVo.setSignStatusName("离线签到");
					}
					planSignStatusVo.setSignOutStatusName(signOutStatus.contains(signTypeStatus) == true ? "签退异常" : "签退正常");
					planSignStatusList.add(planSignStatusVo);
				}
				if (planSignStatusList.size() > 0) {
					planListVo.setPlanSignStatusList(planSignStatusList);
				}
			}

			// 处理实际执行的信息
			List<PlanUserSignDetailVo> realUserSignInfoList = planListVo.getRealUserSignInfoList();
			List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
			if (realUserSignInfoList != null && realUserSignInfoList.size() > 0 && userSignInfoList != null && userSignInfoList.size() > 0) {
				Map<String, List<SignStatusInfoVo>> listMap = userSignInfoList.stream().collect(Collectors.groupingBy(SignStatusInfoVo::getUserName));
				Map<String, List<PlanUserSignDetailVo>> stringListMap = realUserSignInfoList.stream().collect(Collectors.groupingBy(PlanUserSignDetailVo::getUserName));
				List<String> userNames = realUserSignInfoList.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
				for (String userName : userNames) {
					PlanUserSignDetailVo planUserSignDetailVos = stringListMap.get(userName).get(0);
					final List<SignStatusInfoVo> signStatusInfoVos = listMap.get(userName);
					if (signStatusInfoVos != null) {
						final SignStatusInfoVo signStatusInfoVo = signStatusInfoVos.get(0);
						StringBuilder ab = new StringBuilder("(");
						if (Convert.toInt(signStatusInfoVo.getSignStatus(), 0) == SING_STATUS) {
							ab.append("签到异常");
						}
						if (Convert.toInt(signStatusInfoVo.getSignOutStatus(), 0) == SING_STATUS) {
							ab.append(",签退异常");
						}
						ab.append(")");
						planUserSignDetailVos.setUserName(ab.length() > 5 ? userName + ab.toString() : userName);
					}
				}
			}

		}
		iPage.setRecords(planListVos);
		return iPage;
	}

	@Override
	public IPage<PlanScoreListVo> getPlanScoreList(PlanScoreSearchDTO planScoreSearchDTO) {
		Query query = new Query();
		query.setSize(planScoreSearchDTO.getSize());
		query.setCurrent(planScoreSearchDTO.getCurrent());
		IPage<PlanScoreListVo> page = Condition.getPage(query);
		List<PlanScoreListVo> list = baseMapper.getPlanScoreList(page, planScoreSearchDTO);
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
			//处理执行人和实际执行人
			if (planScoreListVo.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_BY.getId()) {
				String userNames = planScoreListVo.getUserNames();
				if (planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus()) {
					planScoreListVo.setPlanUserNames(userNames);
					planScoreListVo.setUserNames(null);
				}
			}
		}
		page.setRecords(list);
		return page;
	}

	@Override
	public IPage<PlanScoreListVo> getDisPlanScoreList(PlanScoreSearchDTO planScoreSearchDTO) {
		Long userId = AuthUtil.getUserId();
		Query query = new Query();
		query.setSize(planScoreSearchDTO.getSize());
		query.setCurrent(planScoreSearchDTO.getCurrent());
		IPage<PlanScoreListVo> page = Condition.getPage(query);
		List<PlanScoreListVo> list = baseMapper.getPlanScoreList(null, planScoreSearchDTO);
		if (list.size() == 0) {
			page.setRecords(list);
			page.setTotal(0);
			return page;
		}
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
			//处理执行人和实际执行人
			if (planScoreListVo.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_BY.getId()) {
				String userNames = planScoreListVo.getUserNames();
				String userIds = planScoreListVo.getUserIds();
				if (planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus()) {
					planScoreListVo.setPlanUserNames(userNames);
					planScoreListVo.setPlanUserIds(userIds);
					planScoreListVo.setUserNames(null);
					planScoreListVo.setUserIds(null);
				}
			}
		}
		List<PlanScoreListVo> planScoreListVo;
		//如果是第二页直接获取redis缓存的内容
		if (planScoreSearchDTO.getCurrent() > 1) {
			planScoreListVo = JSON.parseArray(Convert.toStr(bladeRedis.get(userId.toString() + planScoreSearchDTO.getIsAuto()), "[]"), PlanScoreListVo.class);
		} else {
			//计算抽检的比例工单，比如工单1000条，抽检25%=250条
			BigDecimal multiply = Convert.toBigDecimal(list.size()).multiply(planScoreSearchDTO.getRatio());
			Integer size = Convert.toInt(multiply.setScale(0, RoundingMode.HALF_UP));
			//手动从查询工单获取抽检的比例,
			if (planScoreSearchDTO.getIsAuto()) {
				planScoreListVo = list.subList(0, size);
			} else {
				//自动调根据算法返回
				planScoreListVo = planToUserStorePlan(list, planScoreSearchDTO.getRatio(), planScoreSearchDTO.getTypeStatus());
			}
			bladeRedis.set(userId.toString() + planScoreSearchDTO.getIsAuto(), JSON.toJSONString(planScoreListVo));
		}
		//分页
		List<PlanScoreListVo> subList = planScoreListVo.stream().skip((planScoreSearchDTO.getCurrent() - 1) * planScoreSearchDTO.getSize()).limit(planScoreSearchDTO.getSize()).
			collect(Collectors.toList());
		page.setRecords(subList);
		page.setTotal(planScoreListVo.size());
		return page;
	}

	@Override
	public List<Long> getDisPlanScoreAll(PlanScoreSearchDTO planScoreSearchDTO) {
		Long userId = AuthUtil.getUserId();
		Query query = new Query();
		query.setSize(planScoreSearchDTO.getSize());
		query.setCurrent(planScoreSearchDTO.getCurrent());
		IPage<PlanScoreListVo> page = Condition.getPage(query);
		List<PlanScoreListVo> list = baseMapper.getPlanScoreList(null, planScoreSearchDTO);
		if (list.size() == 0) {
			page.setRecords(list);
			page.setTotal(0);
			return new ArrayList<>();
		}
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
			//处理执行人和实际执行人
			if (planScoreListVo.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_BY.getId()) {
				String userNames = planScoreListVo.getUserNames();
				String userIds = planScoreListVo.getUserIds();
				if (planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus()) {
					planScoreListVo.setPlanUserNames(userNames);
					planScoreListVo.setPlanUserIds(userIds);
					planScoreListVo.setUserNames(null);
					planScoreListVo.setUserIds(null);
				}
			}
		}
		List<PlanScoreListVo> planScoreListVo;
		//如果是第二页直接获取redis缓存的内容
		if (planScoreSearchDTO.getCurrent() > 1) {
			planScoreListVo = JSON.parseArray(Convert.toStr(bladeRedis.get(userId.toString() + planScoreSearchDTO.getIsAuto()), "[]"), PlanScoreListVo.class);
		} else {
			//计算抽检的比例工单，比如工单1000条，抽检25%=250条
			BigDecimal multiply = Convert.toBigDecimal(list.size()).multiply(planScoreSearchDTO.getRatio());
			Integer size = Convert.toInt(multiply.setScale(0, RoundingMode.HALF_UP));
			//手动从查询工单获取抽检的比例,
			if (planScoreSearchDTO.getIsAuto()) {
				planScoreListVo = list.subList(0, size);
			} else {
				//自动调根据算法返回
				planScoreListVo = planToUserStorePlan(list, planScoreSearchDTO.getRatio(), planScoreSearchDTO.getTypeStatus());
			}
			bladeRedis.set(userId.toString() + planScoreSearchDTO.getIsAuto(), JSON.toJSONString(planScoreListVo));
		}
		//分页
		List<PlanScoreListVo> subList = planScoreListVo.stream().skip((planScoreSearchDTO.getCurrent() - 1) * planScoreSearchDTO.getSize()).limit(planScoreSearchDTO.getSize()).
			collect(Collectors.toList());
		page.setRecords(subList);
		page.setTotal(planScoreListVo.size());
		return planScoreListVo.stream().map(PlanScoreListVo::getPlanId).distinct().collect(Collectors.toList());
	}

	@Override
	public List<PlanScoreMonthVo> getPlanScoreMonthList(Date beginTime, Date endTime, Integer typeStatus) {
		List<PlanScoreMonthVo> resultList = new ArrayList<>(6);
		List<MaintainPlanContentExt> list = baseMapper.getPlanScoreMonthList(beginTime, endTime, typeStatus);

		Map<String, List<MaintainPlanContentExt>> listMap = list.stream().collect(Collectors.groupingBy(MaintainPlanContentExt::getGroupPlanDate));
		//表示已评分
		int scoreStatus = 1;

		//循环6个月
		for (int i = 0; i < 6; i++) {
			PlanScoreMonthVo planScoreMonthVo = new PlanScoreMonthVo();
			planScoreMonthVo.setDate(DateUtil.format(DateUtil.offsetMonth(beginTime, i), "yyyy-MM"));
			List<MaintainPlanContentExt> planContentExts = listMap.get(planScoreMonthVo.getDate());
			if (planContentExts != null && planContentExts.size() > 0) {
				planScoreMonthVo.setCounts(planContentExts.size());
				List<MaintainPlanContentExt> contentExts = planContentExts.stream().filter(o -> Convert.toInt(o.getScoreStatus(), 0) == scoreStatus).collect(Collectors.toList());
				planScoreMonthVo.setEnScoreCounts(contentExts.size());
				planScoreMonthVo.setDisScoreCounts(planScoreMonthVo.getCounts() - planScoreMonthVo.getEnScoreCounts());
			}
			resultList.add(planScoreMonthVo);
		}
		return resultList;
	}

	@Override
	public List<PlanScoreListVo> getPlanScoreListDownExcel(PlanScoreSearchDTO planScoreSearchDTO) {
		List<PlanScoreListVo> list = baseMapper.getPlanScoreList(null, planScoreSearchDTO);
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
			//处理执行人和实际执行人
			if (planScoreListVo.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_BY.getId()) {
				String userNames = planScoreListVo.getUserNames();
				String userIds = planScoreListVo.getUserIds();
				if (planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planScoreListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus()) {
					planScoreListVo.setPlanUserNames(userNames);
					planScoreListVo.setPlanUserIds(userIds);
					planScoreListVo.setUserNames(null);
					planScoreListVo.setUserIds(null);
				}
			}
		}
		return list;
	}

	/**
	 * 2023-11-13 事务工单批量设置计划时间
	 *
	 * @param newWorkPlanSetTimeDTO 事务工单id 集合
	 * @return 是否成功
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean setWorkPlanTimes(NewWorkPlanSetTimeDTO newWorkPlanSetTimeDTO) {
		Date planDate = newWorkPlanSetTimeDTO.getPlanDate();
		List<Long> planIds = newWorkPlanSetTimeDTO.getPlanIds();
		List<MaintainPlanContent> planContentList = this.listByIds(planIds);
		// 增加工单限制
		planContentList.forEach(
			t -> {
				Integer status = t.getStatus();
				if (status.equals(TaskStatusEnum.SW_UNDER_WAY.getStatus())
					|| status.equals(TaskStatusEnum.SW_SIGN_FINISH.getStatus())
					|| status.equals(TaskStatusEnum.SW_CLOSED.getStatus())
				) {
					throw new ServiceException("业务异常！不允许修改进行中、已完成、已关闭的工单时间！请重新选择");
				}
			}
		);
		if (!Func.isNull(planDate)) {
			for (MaintainPlanContent maintainPlanContent : planContentList) {
				maintainPlanContent.setPlanDate(planDate);
			}
		}
		return this.updateBatchById(planContentList);
	}


	private List<PlanScoreListVo> planToUserStorePlan(List<PlanScoreListVo> planScoreListVo, BigDecimal ratio, Integer typeStatus) {

		//组装每个人的工单

		List<UserStorePlanInfoVo> resultList = new ArrayList<>(16);


		List<UserStorePlanInfoVo> list = new ArrayList<>(500);
		for (PlanScoreListVo planScoreListVo1 : planScoreListVo) {
			String disUserIds = typeStatus == MaintainPlanContentEnum.MAINTAIN_BY.getId() ? planScoreListVo1.getPlanUserIds() : planScoreListVo1.getUserIds();
			if (StrUtil.isNotEmpty(disUserIds)) {
				List<String> userIds = Arrays.asList(disUserIds.split(","));
				for (String userId : userIds) {
					UserStorePlanInfoVo userStorePlanInfoVo = new UserStorePlanInfoVo();
					userStorePlanInfoVo.setUserId(Convert.toLong(userId));
					userStorePlanInfoVo.setBuildingId(planScoreListVo1.getBuildingId());
					userStorePlanInfoVo.setElevatorId(planScoreListVo1.getElevatorId());
					userStorePlanInfoVo.setPlanId(planScoreListVo1.getPlanId());
					userStorePlanInfoVo.setPlanDate(planScoreListVo1.getPlanDate());
					list.add(userStorePlanInfoVo);
				}
			}
		}
		List<Long> userIds = list.stream().map(UserStorePlanInfoVo::getUserId).distinct().collect(Collectors.toList());
		Map<Long, List<UserStorePlanInfoVo>> listMap = list.stream().collect(Collectors.groupingBy(UserStorePlanInfoVo::getUserId));
		for (Long userId : userIds) {
			List<UserStorePlanInfoVo> userList = new ArrayList<>(16);
			// 第二种情况 比如有6单，3个项目，6台电梯，比例是0.50 抽取3单
			List<UserStorePlanInfoVo> userStorePlanInfoVos = listMap.get(userId);
			List<UserStorePlanInfoVo> userStorePlanInfoVos2 = JSON.parseArray(JSON.toJSONString(userStorePlanInfoVos), UserStorePlanInfoVo.class);
			List<Long> elevatorIds = userStorePlanInfoVos.stream().map(UserStorePlanInfoVo::getElevatorId).distinct().collect(Collectors.toList());
			Map<Long, List<UserStorePlanInfoVo>> elevatorListMap = userStorePlanInfoVos.stream().collect(Collectors.groupingBy(UserStorePlanInfoVo::getElevatorId));
			BigDecimal multiply = Convert.toBigDecimal(userStorePlanInfoVos.size()).multiply(ratio);
			Integer size = Convert.toInt(multiply.setScale(0, RoundingMode.HALF_UP));

			//如果抽取的工单数,大于电梯的数量,随机获取每台电梯的一个工单,不足的数量,随机获取其它相同下的
			/**
			 * 第一种情况计算每个人抽检的工单数  第一种情况 比如有100单，2个项目，5台电梯，比例是0.10 抽取10单
			 *  1:可能1个项目1台电梯只有一单,另外一个项目4台电梯，99单
			 *  2:可能每个项目每台梯的工单比较均匀
			 */

			if (size >= elevatorIds.size()) {
				//获取所有电梯的工单(保证每台梯每个项目都存在,并且不重复)
				for (Long elevatorId : elevatorIds) {
					List<UserStorePlanInfoVo> userStorePlanInfoVos1 = elevatorListMap.get(elevatorId);
					UserStorePlanInfoVo userStorePlanInfoVo = userStorePlanInfoVos1.get(RandomUtil.randomInt(0, userStorePlanInfoVos1.size()));
					userList.add(userStorePlanInfoVo);
					userStorePlanInfoVos2.remove(userStorePlanInfoVo);
				}

				//采用无限循环的方式,获取剩余比例
				boolean found = true;//为false跳出循环
				for (int i = 0; i < userStorePlanInfoVos.size() && found; i++) {
					for (Long elevatorId : elevatorIds) {
						Map<Long, List<UserStorePlanInfoVo>> elevatorListMap2 = userStorePlanInfoVos2.stream().collect(Collectors.groupingBy(UserStorePlanInfoVo::getElevatorId));
						List<UserStorePlanInfoVo> userStorePlanInfoVos1 = elevatorListMap2.get(elevatorId);
						if (userStorePlanInfoVos1 == null || userStorePlanInfoVos1.size() == 0) {
							continue;
						}
						UserStorePlanInfoVo userStorePlanInfoVo = userStorePlanInfoVos1.get(RandomUtil.randomInt(0, userStorePlanInfoVos1.size()));
						userList.add(userStorePlanInfoVo);
						userStorePlanInfoVos2.remove(userStorePlanInfoVo);
						//抽取的比例达到工单数,停止循环
						if (userList.size() >= size) {
							found = false;
							break;
						}
					}
				}
			} else {
				/**
				 * 计算每个人抽检的工单数  第一种情况 比如有100单，3个项目，50台电梯，比例是0.10 抽取10单
				 * 计算每个项目的梯台数占比
				 * 1个项目1台电梯1个工单，2个项目49台电梯
				 * 1个项目2台电梯4个工单，2个项目40台电梯
				 * 10个项目 50台电梯100单，
				 */

				/**
				 * 第一步：计算每个项目的比例
				 * 比如：3个项目10单，每个项目4单,
				 */
				List<Long> buildingIds = userStorePlanInfoVos.stream().map(UserStorePlanInfoVo::getBuildingId).distinct().collect(Collectors.toList());
				BigDecimal divide = Convert.toBigDecimal(size).divide(Convert.toBigDecimal(buildingIds.size()), 0, RoundingMode.UP);
				Map<Long, List<UserStorePlanInfoVo>> buildingMap = userStorePlanInfoVos.stream().collect(Collectors.groupingBy(UserStorePlanInfoVo::getBuildingId));
				for (Long buildingId : buildingIds) {
					//每个项目的电梯数量,电梯数量对比比例,小于等于直接抽取每台电梯的工单,大于计算误差，从公共池里面抽取
					List<Long> elevatorIds2 = buildingMap.get(buildingId).stream().map(UserStorePlanInfoVo::getElevatorId).distinct().collect(Collectors.toList());
					if (elevatorIds2.size() <= Convert.toInt(divide)) {
						for (Long elevatorId : elevatorIds2) {
							List<UserStorePlanInfoVo> userStorePlanInfoVos1 = elevatorListMap.get(elevatorId);
							UserStorePlanInfoVo userStorePlanInfoVo = userStorePlanInfoVos1.get(RandomUtil.randomInt(0, userStorePlanInfoVos1.size()));
							userList.add(userStorePlanInfoVo);
							userStorePlanInfoVos2.remove(userStorePlanInfoVo);
						}
					} else {
						//如果大于就获取指定比例的工单数
						for (int i = 0; i < Convert.toInt(divide); i++) {
							List<UserStorePlanInfoVo> userStorePlanInfoVos1 = elevatorListMap.get(elevatorIds2.get(i));
							UserStorePlanInfoVo userStorePlanInfoVo = userStorePlanInfoVos1.get(RandomUtil.randomInt(0, userStorePlanInfoVos1.size()));
							userList.add(userStorePlanInfoVo);
							userStorePlanInfoVos2.remove(userStorePlanInfoVo);
						}
					}
				}
			}
			//不足的数量，随机获取
			if (userList.size() < size) {
				for (int i = 0; i < userStorePlanInfoVos2.size(); i++) {
					UserStorePlanInfoVo userStorePlanInfoVo = userStorePlanInfoVos2.get(RandomUtil.randomInt(0, userStorePlanInfoVos2.size()));
					userList.add(userStorePlanInfoVo);
					userStorePlanInfoVos2.remove(userStorePlanInfoVo);
					if (userList.size() >= size) {
						break;
					}
				}
			}
			resultList.addAll(userList);
		}
		List<Long> planIds = resultList.stream().map(UserStorePlanInfoVo::getPlanId).distinct().collect(Collectors.toList());
		List<PlanScoreListVo> voList = planScoreListVo.stream().filter(o -> planIds.contains(Convert.toLong(o.getPlanId()))).collect(Collectors.toList());
		return voList;
	}

	@Override
	public ElevatorSimpleInfoVO selectPlanElevatorInfo(Long planId) {
		return baseMapper.selectPlanElevatorInfo(planId);

	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Integer configTemplate(PlanTemplateSubmitDTO submitDTO) {

		List<MaintainPlanTemplate> planTemplates = planTemplateService.list(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, submitDTO.getPlanIds()));

		Map<Long, List<MaintainPlanTemplate>> planGroupMap = planTemplates.stream().collect(Collectors.groupingBy(MaintainPlanTemplate::getPlanId));

		Boolean result = Boolean.FALSE;
		List<MaintainPlanTemplate> resultList = new ArrayList<>();
		List<MaintainPlanTemplate> submitPlanTemplate = submitDTO.getTemplates().stream().sorted(Comparator.comparing(MaintainPlanTemplate::getSort)).collect(Collectors.toList());
		// 新增
		if (submitDTO.getCommitType().equals(1)) {
			planGroupMap.forEach((k, v) -> {
				List<Long> submitTemplateIds = submitPlanTemplate.stream().map(MaintainPlanTemplate::getTemplateId).collect(Collectors.toList());
				List<MaintainPlanTemplate> oldPlanTemplates = v.stream().filter(e -> !submitTemplateIds.contains(e.getTemplateId())).collect(Collectors.toList());
				Integer maxSort = oldPlanTemplates.stream().mapToInt(MaintainPlanTemplate::getSort).max().orElse(0);
				List<MaintainPlanTemplate> newPlanTemplates = new ArrayList<>();
				submitPlanTemplate.forEach(e -> {
					MaintainPlanTemplate oldPlanTemplate = v.stream().filter(j -> e.getTemplateId().equals(j.getTemplateId())).findFirst().orElse(null);
					MaintainPlanTemplate planTemplate = new MaintainPlanTemplate();
					if (Func.isNotEmpty(oldPlanTemplate)) {
						planTemplate.setId(oldPlanTemplate.getId());
					}
					planTemplate.setPlanId(k);
					planTemplate.setTemplateId(e.getTemplateId());
					if (submitDTO.getCommitLocation().equals(2)) {
						planTemplate.setSort(e.getSort() + maxSort);
					} else {
						planTemplate.setSort(e.getSort() - maxSort);
					}
					newPlanTemplates.add(planTemplate);
				});
				resultList.addAll(newPlanTemplates);
			});
			result = planTemplateService.saveOrUpdateBatch(resultList);
		}

		// 替换
		if (submitDTO.getCommitType().equals(2)) {
			planGroupMap.forEach((k, v) -> {
				List<MaintainPlanTemplate> newPlanTemplates = submitPlanTemplate.stream().map(e -> {
					MaintainPlanTemplate planTemplate = new MaintainPlanTemplate();
					planTemplate.setPlanId(k);
					planTemplate.setTemplateId(e.getTemplateId());
					planTemplate.setSort(e.getSort());
					return planTemplate;
				}).collect(Collectors.toList());
				resultList.addAll(newPlanTemplates);
			});
			result = planTemplateService.remove(Wrappers.<MaintainPlanTemplate>lambdaQuery().in(MaintainPlanTemplate::getPlanId, submitDTO.getPlanIds()))
				&& planTemplateService.saveOrUpdateBatch(resultList);
		}

		List<Long> delErrorList = new ArrayList<>();
		// 部分删除
		if (submitDTO.getCommitType().equals(3)) {
			List<Long> delIds = new ArrayList<>();
			planGroupMap.forEach((k, v) -> {
				List<Long> delTemplateIds = submitPlanTemplate.stream().map(MaintainPlanTemplate::getTemplateId).collect(Collectors.toList());
				List<MaintainPlanTemplate> delPlanTemplates = v.stream().filter(e -> delTemplateIds.contains(e.getTemplateId())).collect(Collectors.toList());
				if (v.size() - delPlanTemplates.size() == 0) {
					delErrorList.add(k);
				} else {
					delIds.addAll(delPlanTemplates.stream().map(MaintainPlanTemplate::getId).collect(Collectors.toList()));
				}
			});

			if (Func.isNotEmpty(delIds)) {
				result = planTemplateService.removeByIds(delIds);
			} else {
				result = Boolean.TRUE;
			}
		}

		if (result && submitDTO.getCommitType().equals(3) && delErrorList.size() > 0) {
			bladeRedis.setEx(AuthUtil.getUserId().toString().concat(StringPool.COLON).concat(PLAN_TEMPLATE_BATCH_EXCEL_ERR_CACHE), delErrorList, 30000L);
		}

		if (!result) {
			throw new ServiceException("数据异常,请重试!");
		}
		return delErrorList.size();
	}

	@Override
	public void downBatchDelTemplateErrExcel(HttpServletResponse response) throws IOException {
		Object data = bladeRedis.get(AuthUtil.getUserId().toString().concat(StringPool.COLON).concat(PLAN_TEMPLATE_BATCH_EXCEL_ERR_CACHE));
		if (Func.isEmpty(data)) {
			throw new ServiceException("未有错误数据导出！");
		}

		List<Long> planIds = (List<Long>) data;
		List<MaintainPlanBaseInfoVO> errorList = baseMapper.getMaintainPlanBaseInfosByPlanIds(planIds);
		// 排序
		LinkedList<MaintainPlanBaseInfoVO> errorLinkedList = errorList.stream().sorted(Comparator.comparing(MaintainPlanBaseInfoVO::getElevatorId).thenComparing(MaintainPlanBaseInfoVO::getPlanDate)).collect(Collectors.toCollection(LinkedList::new));

		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			LinkedList<List<String>> rows = new LinkedList<>();
			List<String> title = CollUtil.newArrayList("计划日期", "项目", "梯号", "保养计划", "保养类型");
			// 封装导出数据
			for (MaintainPlanBaseInfoVO errorDate : errorLinkedList) {
				List<String> row = new ArrayList<>(8);
				row.add(errorDate.getPlanDate());
				row.add(errorDate.getBuildingName());
				row.add(errorDate.getElevatorAddress());
				row.add(errorDate.getTaskPlanName());
				row.add(errorDate.getTemplateName());
				rows.add(row);
			}
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.setColumnWidth(0, 30);
			excelWriter.setColumnWidth(1, 30);
			excelWriter.setColumnWidth(2, 30);
			excelWriter.setColumnWidth(3, 30);
			excelWriter.setColumnWidth(4, 30);

			// 单独设置头部
			excelWriter.writeHeadRow(title);
			String fileName = "删除人员失败排期计划_" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			CommonUtil.setResponseHeader(response, fileName);
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@Override
	public IPage<PlanScoreListVo> getPlanScoreListWx(PlanScoreSearchDTO planScoreSearchDTO) {
		Query query = new Query();
		query.setSize(planScoreSearchDTO.getSize());
		query.setCurrent(planScoreSearchDTO.getCurrent());
		IPage<PlanScoreListVo> page = Condition.getPage(query);
		List<PlanScoreListVo> list = baseMapper.getPlanScoreListWx(page, planScoreSearchDTO);
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
		}
		page.setRecords(list);
		return page;
	}

	@Override
	public List<PlanSourceVO> getPlanSourceList(Long planId) {
		if (Func.isNull(planId)) {
			throw new ServiceException("请传入planId");
		}
		// 根据当前id 获取 关联列表
		List<Long> fromPlanIdByOwnId = getFromPlanIdByOwnId(planId);
		if (Func.isEmpty(fromPlanIdByOwnId)) {
			return null;
		}
		List<PlanSourceVO> result = baseMapper.getPlanSourceMessagesByIds(fromPlanIdByOwnId);
		// 处理每行数据命名赋值与执行人;若是保养任务，需要多返回planCheckType， planCheckName
		if (Func.isNotEmpty(result)) {
			result.forEach(f -> {
				f.setSourcePlanTypeName(MaintainPlanContentEnum.getName(f.getSourcePlanType()));
				// 执行人
				List<String> executeUserNames = baseMapper.getExecuteUserNamesByPlanId(f.getSourcePlanId());
				executeUserNames = executeUserNames.stream().distinct().collect(Collectors.toList());
				if (Func.isNotEmpty(executeUserNames)) {
					f.setSourceActualPerson(String.join(",", executeUserNames));
				}
//				// 若是保养工单,新返回checkName, checkType
//				if(f.getSourcePlanType().equals(MaintainPlanContentEnum.MAINTAIN_CHECK.getId())){
//
//				}
			});
		}
		return result;
	}

	/**
	 * 初始化计划来源列表
	 *
	 * @return Boolean 是否成功
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean initPlanSourceList() {
		// 1. 筛选出所有维修工单，且来源为空
		List<MaintainPlanContent> planList = baseMapper.listEmptyPlanSource();
		// 2. 遍历筛选出来的工单：若存在关联工单，将关联工单来源类型插入来源，否则默认为维修
		planList.forEach(p -> {
			Long ownPlanId = p.getId();
			List<MaintainPlanRelation> relationList = planRelationService.list(
				new LambdaQueryWrapper<MaintainPlanRelation>()
					.eq(MaintainPlanRelation::getOwnPlanId, ownPlanId)
			);
			if (Func.isNotEmpty(relationList)) {
				if (relationList.size() > 1) {
					throw new ServiceException(ownPlanId + "数据异常，不应存在多个关联工单，请手动处理！");
				}
				Long fromPlanId = relationList.get(0).getFromPlanId();
				MaintainPlanContent fromPlan = this.getById(fromPlanId);
				if (Func.notNull(fromPlan)) {
					p.setSourcePlanType(fromPlan.getTypeStatus());
				}
			} else {
				p.setSourcePlanType(MaintainPlanContentEnum.MAINTAIN_WX.getId());
			}
		});
		// 3. 执行更新
		return this.updateBatchById(planList);
	}

	/**
	 * 逾期风险，仅银河飞书使用，复用分页逻辑
	 *
	 * @param planSearchDTO 查询条件
	 * @return IPage<PlanListVo> 计划列表
	 */
	@Override
	@TenantIgnore
	public List<PlanListVo> planListNewLark(PlanSearchDTO planSearchDTO) {
		//表示签退签到异常
		int SING_STATUS = 2;
		if (Func.isEmpty(planSearchDTO.getPlanStatus())) {
			List<Integer> planStatus = TaskStatusEnum.getTaskStatusByType(MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.stream().map(TaskStatusVO::getStatus).collect(Collectors.toList());
			planSearchDTO.setPlanStatus(planStatus);
		}
		List<PlanListVo> planListVos = baseMapper.selectByPlanListLark(planSearchDTO);

		final List<Long> planIds = planListVos.stream().filter(o -> o.getPlanStatus() != TaskStatusEnum.BY_DISABLE.getStatus()).map(PlanListVo::getPlanId).collect(Collectors.toList());
		List<MaintainSign> maintainSignList = new ArrayList<>(16);
		if (!planIds.isEmpty()) {
			maintainSignList = maintainSignService.getMaintainSignList(planIds);
		}
		//处理逾期未逾期的判断逻辑
		//表示年审工单
		Integer auditType = 2;
		//逾期类型1- 非年审梯,1:已逾期,2:未逾期
		Integer[] overdueTypes = {0, -1, 1, 2};
		for (PlanListVo planListVo : planListVos) {
			if (Func.isEmpty(planListVo.getOverdueDay())) {
				planListVo.setOverdueDayName("及时");
			}
			planListVo.setOverdueType(overdueTypes[1]);
			if (auditType.equals(planListVo.getAuditType())) {
				//1:工单时间如果大于今天 表示未逾期
				Integer date1 = Convert.toInt(DateUtil.format(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), "yyyyMMdd"));
				Integer date2 = Convert.toInt(DateUtil.format(new Date(), "yyyyMMdd"));
				if (date1 > date2) {
					planListVo.setOverdueType(overdueTypes[3]);
					long day = DateUtil.betweenDay(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), new Date(), true);
					if (day == 1L) {
						planListVo.setIsOverdueTomorrow(true);
					}
				} else {
					Date date = planListVo.getMaintainSignatureTime() != null ? planListVo.getMaintainSignatureTime() : new Date();
					Integer date3 = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
					Integer date4 = Convert.toInt(DateUtil.format(planListVo.getOverdueDate(), "yyyyMMdd"));
					if (date3 > date4) {
						planListVo.setOverdueType(overdueTypes[2]);
					} else {
						//如果维保签字时间和临界时间相等表示未逾期
						if (date3.equals(date4)) {
							planListVo.setOverdueType(overdueTypes[3]);
							long day = DateUtil.betweenDay(date, DateUtil.tomorrow(), true);
							if (day == 1L) {
								planListVo.setIsOverdueTomorrow(true);
							}
						} else if (date4 < Convert.toInt(DateUtil.format(date, "yyyyMMdd"))) {
							planListVo.setOverdueType(overdueTypes[2]);
						} else {
							//临界时间大于维保签字时间未逾期
							planListVo.setOverdueType(overdueTypes[3]);
							//临界值小于明天，然后判断时间差
							long day = DateUtil.betweenDay(planListVo.getOverdueDate(), new Date(), true);
							if (day == 1L) {
								planListVo.setIsOverdueTomorrow(true);
							}

						}
					}
				}
			}

			if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
				List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
				List<SignStatusInfoVo> resultList = new ArrayList<>(5);
				if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
					List<PlanUserSignDetailVo> userSignInfoList2 = planListVo.getUserSignInfoList2();
					List<String> collect = userSignInfoList2.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
					if (userSignInfoList != null && userSignInfoList.size() > 0) {
						for (SignStatusInfoVo signStatusInfoVo : userSignInfoList) {
							if (collect.contains(signStatusInfoVo.getUserName())) {
								resultList.add(signStatusInfoVo);
							}
							planListVo.setUserSignInfoList(resultList);
						}
					}

				}
			}
			//处理每个用户的签到信息
			if (!planIds.isEmpty()) {
				final Map<Long, List<MaintainSign>> map = maintainSignList.stream().collect(Collectors.groupingBy(MaintainSign::getPlanId));
				final List<MaintainSign> maintainSigns = map.get(planListVo.getPlanId());
				if (maintainSigns == null) {
					continue;
				}
				final List<Long> userIds = maintainSigns.stream().map(MaintainSign::getUserId).distinct().collect(Collectors.toList());
				final Map<Long, List<MaintainSign>> map1 = maintainSigns.stream().collect(Collectors.groupingBy(MaintainSign::getUserId));
				int signTypeStatus = 2;//表示正常
				List<PlanSignStatusVo> planSignStatusList = new ArrayList<>(5);
				for (Long userId : userIds) {
					List<MaintainSign> maintainSigns1 = map1.get(userId);
					final List<Integer> signStatus = maintainSigns1.stream().map(MaintainSign::getSignStatus).distinct().collect(Collectors.toList());
					final List<Integer> signOutStatus = maintainSigns1.stream().map(MaintainSign::getSignOutStatus).distinct().collect(Collectors.toList());
					PlanSignStatusVo planSignStatusVo = new PlanSignStatusVo();
					planSignStatusVo.setUserName(maintainSigns1.get(0).getUserName());
					planSignStatusVo.setSignStatusName(signStatus.contains(signTypeStatus) == true ? "签到异常" : "签到正常");
					// 离线签到
					if (signStatus.contains(3)) {
						planSignStatusVo.setSignStatusName("离线签到");
					}
					planSignStatusVo.setSignOutStatusName(signOutStatus.contains(signTypeStatus) == true ? "签退异常" : "签退正常");
					planSignStatusList.add(planSignStatusVo);
				}
				if (planSignStatusList.size() > 0) {
					planListVo.setPlanSignStatusList(planSignStatusList);
				}
			}

			//处理实际执行的信息
			List<PlanUserSignDetailVo> realUserSignInfoList = planListVo.getRealUserSignInfoList();
			List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
			if (realUserSignInfoList != null && !realUserSignInfoList.isEmpty() && userSignInfoList != null && userSignInfoList.size() > 0) {
				Map<String, List<SignStatusInfoVo>> listMap = userSignInfoList.stream().collect(Collectors.groupingBy(SignStatusInfoVo::getUserName));
				Map<String, List<PlanUserSignDetailVo>> stringListMap = realUserSignInfoList.stream().collect(Collectors.groupingBy(PlanUserSignDetailVo::getUserName));
				List<String> userNames = realUserSignInfoList.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
				for (String userName : userNames) {
					PlanUserSignDetailVo planUserSignDetailVos = stringListMap.get(userName).get(0);
					final List<SignStatusInfoVo> signStatusInfoVos = listMap.get(userName);
					if (signStatusInfoVos != null) {
						final SignStatusInfoVo signStatusInfoVo = signStatusInfoVos.get(0);
						StringBuilder ab = new StringBuilder("(");
						if (Convert.toInt(signStatusInfoVo.getSignStatus(), 0) == SING_STATUS) {
							ab.append("签到异常");
						}
						if (Convert.toInt(signStatusInfoVo.getSignOutStatus(), 0) == SING_STATUS) {
							ab.append(",签退异常");
						}
						ab.append(")");
						planUserSignDetailVos.setUserName(ab.length() > 5 ? userName + ab.toString() : userName);
					}
				}
			}
		}
		return planListVos;
	}

	private void isOverdueTomorrow(Date date1, Date date2, PlanListVo planListVo) {
		if (planListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus() || planListVo.getPlanStatus() == TaskStatusEnum.BY_FINISH.getStatus()) {
			long day = DateUtil.betweenDay(date1, date2, true);
			if (day == 0) {
				planListVo.setIsOverdueTomorrow(true);
			}

		}
	}

	@Override
	@TenantIgnore
	public List<LarkRepairVO> getLarkRepairVOListByPlanId(Long planId) {
		// 获取飞书 维修工单列表
		return baseMapper.getLarkRepairVOListByPlanId(planId);
	}

	@Override
	@TenantIgnore
	public int troubleCount(Long elevatorId, Date beginTime, Date endTime) {
		return baseMapper.troubleCount(elevatorId, beginTime, endTime);
	}

	@Override
	public ElevatorStopPlanSimpleDTO selectElevatorStopPlanSimple(Long planId) {
		return baseMapper.selectElevatorStopPlanSimple(planId);
	}

	/**
	 * 逾期风险，仅银河飞书使用，复用分页逻辑
	 *
	 * @param planSearchDTO 查询条件
	 * @return IPage<PlanListVo> 计划列表
	 */
	@Override
	@TenantIgnore
	public List<PlanListVo> planListNewLarkTemp(PlanSearchDTO planSearchDTO) {
		//表示签退签到异常
		int SING_STATUS = 2;
		if (Func.isEmpty(planSearchDTO.getPlanStatus())) {
			List<Integer> planStatus = TaskStatusEnum.getTaskStatusByType(MaintainPlanContentEnum.MAINTAIN_BY.getId())
				.stream().map(TaskStatusVO::getStatus).collect(Collectors.toList());
			planSearchDTO.setPlanStatus(planStatus);
		}
		List<PlanListVo> planListVos = baseMapper.selectByPlanListLarkTemp(planSearchDTO);

		final List<Long> planIds = planListVos.stream().filter(o -> o.getPlanStatus() != TaskStatusEnum.BY_DISABLE.getStatus()).map(PlanListVo::getPlanId).collect(Collectors.toList());
		List<MaintainSign> maintainSignList = new ArrayList<>(16);
		if (!planIds.isEmpty()) {
			maintainSignList = maintainSignService.getMaintainSignList(planIds);
		}
		//处理逾期未逾期的判断逻辑
		//表示年审工单
		Integer auditType = 2;
		//逾期类型1- 非年审梯,1:已逾期,2:未逾期
		Integer[] overdueTypes = {0, -1, 1, 2};
		for (PlanListVo planListVo : planListVos) {
			if (Func.isEmpty(planListVo.getOverdueDay())) {
				planListVo.setOverdueDayName("及时");
			}
			planListVo.setOverdueType(overdueTypes[1]);
			if (auditType.equals(planListVo.getAuditType())) {
				//1:工单时间如果大于今天 表示未逾期
				Integer date1 = Convert.toInt(DateUtil.format(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), "yyyyMMdd"));
				Integer date2 = Convert.toInt(DateUtil.format(new Date(), "yyyyMMdd"));
				if (date1 > date2) {
					planListVo.setOverdueType(overdueTypes[3]);
					long day = DateUtil.betweenDay(DateUtil.parse(planListVo.getPlanDate(), "yyyy-MM-dd"), new Date(), true);
					if (day == 1L) {
						planListVo.setIsOverdueTomorrow(true);
					}
				} else {
					Date date = planListVo.getMaintainSignatureTime() != null ? planListVo.getMaintainSignatureTime() : new Date();
					Integer date3 = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
					Integer date4 = Convert.toInt(DateUtil.format(planListVo.getOverdueDate(), "yyyyMMdd"));
					if (date3 > date4) {
						planListVo.setOverdueType(overdueTypes[2]);
					} else {
						//如果维保签字时间和临界时间相等表示未逾期
						if (date3.equals(date4)) {
							planListVo.setOverdueType(overdueTypes[3]);
							long day = DateUtil.betweenDay(date, DateUtil.tomorrow(), true);
							if (day == 1L) {
								planListVo.setIsOverdueTomorrow(true);
							}
						} else if (date4 < Convert.toInt(DateUtil.format(date, "yyyyMMdd"))) {
							planListVo.setOverdueType(overdueTypes[2]);
						} else {
							//临界时间大于维保签字时间未逾期
							planListVo.setOverdueType(overdueTypes[3]);
							//临界值小于明天，然后判断时间差
							long day = DateUtil.betweenDay(planListVo.getOverdueDate(), new Date(), true);
							if (day == 1L) {
								planListVo.setIsOverdueTomorrow(true);
							}

						}
					}
				}
			}

			if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
				List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
				List<SignStatusInfoVo> resultList = new ArrayList<>(5);
				if (StrUtil.isNotEmpty(planListVo.getUserSignInfos2())) {
					List<PlanUserSignDetailVo> userSignInfoList2 = planListVo.getUserSignInfoList2();
					List<String> collect = userSignInfoList2.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
					if (userSignInfoList != null && userSignInfoList.size() > 0) {
						for (SignStatusInfoVo signStatusInfoVo : userSignInfoList) {
							if (collect.contains(signStatusInfoVo.getUserName())) {
								resultList.add(signStatusInfoVo);
							}
							planListVo.setUserSignInfoList(resultList);
						}
					}

				}
			}
			//处理每个用户的签到信息
			if (!planIds.isEmpty()) {
				final Map<Long, List<MaintainSign>> map = maintainSignList.stream().collect(Collectors.groupingBy(MaintainSign::getPlanId));
				final List<MaintainSign> maintainSigns = map.get(planListVo.getPlanId());
				if (maintainSigns == null) {
					continue;
				}
				final List<Long> userIds = maintainSigns.stream().map(MaintainSign::getUserId).distinct().collect(Collectors.toList());
				final Map<Long, List<MaintainSign>> map1 = maintainSigns.stream().collect(Collectors.groupingBy(MaintainSign::getUserId));
				int signTypeStatus = 2;//表示正常
				List<PlanSignStatusVo> planSignStatusList = new ArrayList<>(5);
				for (Long userId : userIds) {
					List<MaintainSign> maintainSigns1 = map1.get(userId);
					final List<Integer> signStatus = maintainSigns1.stream().map(MaintainSign::getSignStatus).distinct().collect(Collectors.toList());
					final List<Integer> signOutStatus = maintainSigns1.stream().map(MaintainSign::getSignOutStatus).distinct().collect(Collectors.toList());
					PlanSignStatusVo planSignStatusVo = new PlanSignStatusVo();
					planSignStatusVo.setUserName(maintainSigns1.get(0).getUserName());
					planSignStatusVo.setSignStatusName(signStatus.contains(signTypeStatus) == true ? "签到异常" : "签到正常");
					// 离线签到
					if (signStatus.contains(3)) {
						planSignStatusVo.setSignStatusName("离线签到");
					}
					planSignStatusVo.setSignOutStatusName(signOutStatus.contains(signTypeStatus) == true ? "签退异常" : "签退正常");
					planSignStatusList.add(planSignStatusVo);
				}
				if (planSignStatusList.size() > 0) {
					planListVo.setPlanSignStatusList(planSignStatusList);
				}
			}

			//处理实际执行的信息
			List<PlanUserSignDetailVo> realUserSignInfoList = planListVo.getRealUserSignInfoList();
			List<SignStatusInfoVo> userSignInfoList = planListVo.getUserSignInfoList();
			if (realUserSignInfoList != null && !realUserSignInfoList.isEmpty() && userSignInfoList != null && userSignInfoList.size() > 0) {
				Map<String, List<SignStatusInfoVo>> listMap = userSignInfoList.stream().collect(Collectors.groupingBy(SignStatusInfoVo::getUserName));
				Map<String, List<PlanUserSignDetailVo>> stringListMap = realUserSignInfoList.stream().collect(Collectors.groupingBy(PlanUserSignDetailVo::getUserName));
				List<String> userNames = realUserSignInfoList.stream().map(PlanUserSignDetailVo::getUserName).collect(Collectors.toList());
				for (String userName : userNames) {
					PlanUserSignDetailVo planUserSignDetailVos = stringListMap.get(userName).get(0);
					final List<SignStatusInfoVo> signStatusInfoVos = listMap.get(userName);
					if (signStatusInfoVos != null) {
						final SignStatusInfoVo signStatusInfoVo = signStatusInfoVos.get(0);
						StringBuilder ab = new StringBuilder("(");
						if (Convert.toInt(signStatusInfoVo.getSignStatus(), 0) == SING_STATUS) {
							ab.append("签到异常");
						}
						if (Convert.toInt(signStatusInfoVo.getSignOutStatus(), 0) == SING_STATUS) {
							ab.append(",签退异常");
						}
						ab.append(")");
						planUserSignDetailVos.setUserName(ab.length() > 5 ? userName + ab.toString() : userName);
					}
				}
			}
		}
		return planListVos;
	}

	@Override
	public ElevatorRescueRobotSimpleVO getElevatorSimpleInfoByPlanId(Long planId) {
		return baseMapper.getElevatorSimpleInfoByPlanId(planId);
	}

	@Override
	public List<ElevatorEffectedVO> selectElevatorEffectedListByDate(Date beginTime, Date endTime) {
		return baseMapper.selectElevatorEffectedListByDate(beginTime, endTime);
	}

	@Override
	public List<ElevatorEffectedVO> selectElevatorEffectedListByEndDate(String endTime) {
		return baseMapper.selectElevatorEffectedListByEndDate(endTime);
	}

	@Override
	public List<SidaMaintainPlanVO> getMaintainPlanListByTenantIdAndTime(String tenantId, Date startTime, Date endTime) {
		return baseMapper.getMaintainPlanListByTenantIdAndTime(tenantId, startTime, endTime);
	}

	@Override
	public List<UserPerformanceMonthPlanListDTO> listUserPerformancePlanList(String lastMonth, String tenantId) {
		return baseMapper.listUserPerformancePlanList(lastMonth, tenantId);
	}

	@Override
	public List<FeiShuUserDeptElevatorWxPlanDTO> selectUserDeptElevatorWxPlan(String lastMonth, List<Long> userIds) {
		return baseMapper.selectUserDeptElevatorWxPlan(lastMonth, userIds);
	}

	@Override
	public List<ElevatorMaintainCostPlanListDTO> selectElevatorMaintainCostPlanListMonth(String tenantId, String lastMonth, List<Long> elevatorIds) {
		return baseMapper.selectElevatorMaintainCostPlanListMonth(tenantId, lastMonth, elevatorIds);
	}

	@Override
	@TenantIgnore
	public List<BILarkRescueCountSourceVO> getGalaxyRescueResource(Date startDate, Date endDate) {
		return baseMapper.getGalaxyRescueResource(startDate, endDate);
	}

	@Override
	public List<BiWxPlanListDTO> selectBiWxPlanList(Date startDate, Date endDate) {
		return baseMapper.selectBIWxPlanList(startDate, endDate);
	}

	@Override
	public List<ElevatorSimpleInfoVO> selectElevatorStopByPlan(List<Long> planIds) {
		return baseMapper.selectElevatorStopByPlan(planIds);
	}

	@Override
	public List<BiWxPlanListDTO> selectBiWxPlanUserList(List<Long> planIds) {
		return baseMapper.selectBiWxPlanUserList(planIds);
	}

	@Override
	public List<BiWxPlanListDTO> selectElevatorDeptPlan(Date startDate, Date endDate) {
		return baseMapper.selectElevatorDeptPlan(startDate, endDate);
	}

	@Override
	public List<BiWxPlanListDTO> selectElevatorUserPlan(Date startDate, Date endDate) {
		return baseMapper.selectElevatorUserPlan(startDate, endDate);
	}

	@Override
	public IPage<PlanScoreListVo> getDisPlanScoreListWx(PlanScoreSearchDTO planScoreSearchDTO) {
		Long userId = AuthUtil.getUserId();
		Query query = new Query();
		query.setSize(planScoreSearchDTO.getSize());
		query.setCurrent(planScoreSearchDTO.getCurrent());
		IPage<PlanScoreListVo> page = Condition.getPage(query);
		List<PlanScoreListVo> list = baseMapper.getPlanScoreListWx(null, planScoreSearchDTO);
		if (list.size() == 0) {
			page.setRecords(list);
			page.setTotal(0);
			return page;
		}
		for (PlanScoreListVo planScoreListVo : list) {
			planScoreListVo.setPlanStatusName(TaskStatusEnum.getTaskName(planScoreListVo.getTypeStatus(), planScoreListVo.getPlanStatus()));
		}
		List<PlanScoreListVo> planScoreListVo;
		//如果是第二页直接获取redis缓存的内容
		if (planScoreSearchDTO.getCurrent() > 1) {
			planScoreListVo = JSON.parseArray(Convert.toStr(bladeRedis.get(userId.toString() + "wx" + planScoreSearchDTO.getIsAuto()), "[]"), PlanScoreListVo.class);
		} else {
			//计算抽检的比例工单，比如工单1000条，抽检25%=250条
			BigDecimal multiply = Convert.toBigDecimal(list.size()).multiply(planScoreSearchDTO.getRatio());
			Integer size = Convert.toInt(multiply.setScale(0, RoundingMode.HALF_UP));
			//手动从查询工单获取抽检的比例,
			if (planScoreSearchDTO.getIsAuto()) {
				planScoreListVo = list.subList(0, size);
			} else {
				//自动调根据算法返回
				planScoreListVo = planToUserStorePlan(list, planScoreSearchDTO.getRatio(), planScoreSearchDTO.getTypeStatus());
			}
			bladeRedis.set(userId.toString() + "wx" + planScoreSearchDTO.getIsAuto(), JSON.toJSONString(planScoreListVo));
		}
		//分页
		List<PlanScoreListVo> subList = planScoreListVo.stream().skip((planScoreSearchDTO.getCurrent() - 1) * planScoreSearchDTO.getSize()).limit(planScoreSearchDTO.getSize()).
			collect(Collectors.toList());
		page.setRecords(subList);
		page.setTotal(planScoreListVo.size());
		return page;
	}

	@Override
	public List<ElevatorPlanUserDTO> selectCountByPlanUserSignature(String tenantId) {
		return baseMapper.selectCountByPlanUserSignature(tenantId);
	}

	@Override
	public List<ElevatorPlanUserDTO> selectCountUserByPlanOverdue(String tenantId) {
		return baseMapper.selectCountUserByPlanOverdue(tenantId);
	}

	@Override
	public List<BiWxPlanListDTO> selectWxFaultPlanList(Date startDate, Date endDate) {
		return baseMapper.selectWxFaultPlanList(startDate, endDate);
	}

	@Override
	public List<BiWxPlanListDTO> selectWxFaultPlanListAll(Date startDate, Date endDate) {
		return baseMapper.selectWxFaultPlanListAll(startDate, endDate);
	}


	/**
	 * 根据电梯id获取工单
	 *
	 * @param eleId     电梯id
	 * @param beginTime 开始时间
	 * @param endTime   结束时间
	 * @return 工单列表
	 */
	@Override
	public List<MaintainPlanContent> getPlanListOfElevatorId(Long eleId, Date beginTime, Date endTime) {
		return baseMapper.getPlanListOfElevatorId(eleId, beginTime, endTime);
	}

	@Override
	public String selectNextByDate(Long planId) {
		return baseMapper.selectNextByDate(planId);
	}

	@Override
	public List<VerifyModifyPlanUserResultVO> selectVerifyModifyPlanUser(BatchModifyPlanUserDTO userDTO) {
		return baseMapper.selectVerifyModifyPlanUser(userDTO);
	}

	@Override
	public List<PlanUserAllDTO> selectPlanUserAll(List<Long> planIds) {
		return baseMapper.selectPlanUserAll(planIds);
	}

	@Override
	public List<Long> selectElevatorByCollectPlanIds(SugarSearchDTO searchDTO) {
		return baseMapper.selectElevatorByCollectPlanIds(searchDTO);
	}

	@Override
	public List<ElevatorByCollectListDTO> selectElevatorByCollectList(List<Long> planIds) {
		return baseMapper.selectElevatorByCollectList(planIds);
	}

	@Override
	public List<ElevatorByCollectListDTO> selectElevatorByCollectCorrelationZgList(List<Long> planIds) {
		return baseMapper.selectElevatorByCollectCorrelationZgList(planIds);
	}

	@Override
	public List<ElevatorByCollectListDTO> selectElevatorByCollectCorrelationZgStatusList(SugarSearchDTO searchDTO) {
		return baseMapper.selectElevatorByCollectCorrelationZgStatusList(searchDTO);
	}
}
